局域网UDP通讯丢包

吹雪 2012-10-23 03:10:17
两台电脑,连接在同一个路由器上。
设发送端电脑为A,接收端为B。server运行在A上,两个客户端client1(使用9012端口)和client2(使用9011端口)运行在B上。

A使用9001端口给B电脑的9012和9011端口发送两条相同的数据包(数据长度为11字节,使用sendto发送,先发送到9012端口,再发送到9011端口)

确认网络状况良好,电脑状况良好,代码无问题:

int len_head =sizeof(modbus->stx)+sizeof(modbus->address)+sizeof(modbus->command)+sizeof(modbus->len);
int len_data = modbus->getDataLength();
int len_tail = sizeof(modbus->fcs);
int length = len_head+len_data+len_tail;

if(length > MAX_UDP_PACKET_SIZE)
return X_UDP_TOOMUCH_DATA;

CHAR* pBuf = (CHAR*)_alloca(length);
memcpy(pBuf,modbus,len_head);
if(len_data>0)
memcpy(pBuf+len_head,modbus->data,len_data);
memcpy(pBuf+len_head+len_data,modbus->fcs,len_tail);

int total = 0;
while(total < length)
{
int sended = sendto(g_socket,pBuf+total,length-total,0,(sockaddr*)addr,sizeof(sockaddr));
if(sended <= 0)
break;

total+=sended;
}

return total==length?X_UDP_OK:X_UDP_WRITE_FAILED;


现在碰到的问题是:

server程序刚启动时,server发送到client1和client2的指令均可以被成功接收;如果一直发送(每次都是先给client1发送,接着马上给client2发送)没有问题;
但是如果server软件间隔几十分钟不操作后,server再次发送指令到client1和client2,会发现client1无法接收到任何数据,而client2正常接收再次操作,发送client1和client2都可以正常接收。
用SNIFFER运行在B电脑上监控网络数据,发现只有一个发到9011端口的UDP包,而发往9012端口的包无影无踪。在发到9011端口的数据包前有一个WINS Name Service包(这个应该没有影响)。
用IRIS软件监控A电脑上的数据,亦只有发现一个发往B电脑9011端口的UDP包,发往9012端口的包在server段亦未监控到

在代码内有OutputDebugString辅助调试,可以确认有调用sendto给B电脑的9012端口发数据
这会是什么问题呢?
...全文
956 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
Joseph-Growth 2013-01-24
  • 打赏
  • 举报
回复
不知你接收端是怎么处理的。你用了一个套接字,为两个端口发送数据。这两个数据用的是同一个缓冲区。试一试用两个套接字,分别负责两个端口,看是否还是这样。
这不是鸭头 2012-10-29
  • 打赏
  • 举报
回复
要自己对丢失的包进行重传处理
UDX协议 2012-10-29
  • 打赏
  • 举报
回复
你这个也算是偶然现象吧,UDP丢包,有时很有随即性的。
吹雪 2012-10-29
  • 打赏
  • 举报
回复
现在发现的规律是,前面两个数据包丢失,如果发三次,第三个肯定能收到。

如果在本机发送,则永不丢失。
吹雪 2012-10-26
  • 打赏
  • 举报
回复
好吧,我换个问法:什么情况下会导致udp socket sendto成功而网卡并发发出数据?


sendto返回值为待发送的字节数(11),标明sendto已成功。ip地址和端口号绝对正确,什么情况下会导致该数据包并未被提交到网卡驱动?(使用sniffer在发送端监控,没有发现该数据包)。

而且不是每次都丢,是偶尔丢失(经过若干分钟无socket操作后,再次发送就有可能丢失。)如果将接收端设在本机,则永不丢失。
xjchilli 2012-10-26
  • 打赏
  • 举报
回复
帮顶,同求解答。。
吹雪 2012-10-24
  • 打赏
  • 举报
回复
加上发送prefix的代码后问题依旧,prefix和第一个包依然是不知所踪。

另外,removeFirstRequest的实现如下:

LPSOCKET_REQUEST removeFirstRequest()
{
EnterCriticalSection( &g_cs );
LPSOCKET_REQUEST pRequest = NULL;

__try
{
if( true == g_listRequests.empty())
__leave;

pRequest = g_listRequests.front();
g_listRequests.pop_front();
}
__finally
{
LeaveCriticalSection( &g_cs );
}

return pRequest;
}

typedef std::list<LPDEVICE_REQUEST> LISTREQUEST;
static LISTREQUEST g_listRequests;
吹雪 2012-10-24
  • 打赏
  • 举报
回复
上面的sendto(g_socket,prefix,sizeof(prefix),0,(sockaddr*)addr,sizeof(sockaddr));是因为我发现每次长时间不操作后都是丢失第一个包,所以人为的在所有指令前插入一条无效指令。
吹雪 2012-10-24
  • 打赏
  • 举报
回复

extern "C" BYTE XUDPMANAGER_API __stdcall UdpInsertRequest2(LPSOCKET_REQUEST request)
{
EnterCriticalSection( &g_cs );
__try
{
g_listRequests.push_back(request);
}
__finally
{
LeaveCriticalSection( &g_cs );
}

if(g_eventWork)
SetEvent(g_eventWork);

return X_UDP_OK;
}

int writeModbusPackage(sockaddr_in *addr,LPMODBUS_PACKAGE modbus)
{
// 一次性发送,接收端使用ReadFile(...overlapped)接收时会报错ERROR_MORE_DATA
static CHAR prefix[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};
sendto(g_socket,prefix,sizeof(prefix),0,(sockaddr*)addr,sizeof(sockaddr));
//////////////////////////////////////////////////////////////////////////
//1.3 版后去掉etx段
int len_head =sizeof(modbus->stx)+sizeof(modbus->address)+sizeof(modbus->command)+sizeof(modbus->len);
int len_data = modbus->getDataLength();
int len_tail = sizeof(modbus->fcs);
int length = len_head+len_data+len_tail;

if(length > MAX_UDP_PACKET_SIZE)
return X_UDP_TOOMUCH_DATA;

CHAR* pBuf = (CHAR*)_alloca(length);
memcpy(pBuf,modbus,len_head);
if(len_data>0)
memcpy(pBuf+len_head,modbus->data,len_data);
memcpy(pBuf+len_head+len_data,modbus->fcs,len_tail);

int total = 0;
while(total < length)
{
int sended = sendto(g_socket,pBuf+total,length-total,0,(sockaddr*)addr,sizeof(sockaddr));
if(sended <= 0)
break;

total+=sended;
}

return total==length?X_UDP_OK:X_UDP_WRITE_FAILED;
}

unsigned int __stdcall threadProc(LPVOID)
{
traceline(TEXT("udp worker thread started.\n"));

static sockaddr_in addr;
memset(addr.sin_zero,0,sizeof(addr.sin_zero));
addr.sin_family=AF_INET;

HANDLE handles[2] = {g_eventExit,g_eventWork};
while(true)
{
DWORD result = WaitForMultipleObjects(2,handles,FALSE,INFINITE);
if(result == WAIT_OBJECT_0)
break;
else if(result == WAIT_OBJECT_0+1)
{
while(!g_listRequests.empty())
{
LPSOCKET_REQUEST request = removeFirstRequest();
if(!request)
break;

LPDEVICE_REQUEST devReq = request->pRequest;
MODBUS_PACKAGE modbus(devReq);

addr.sin_addr.s_addr=request->ip;
addr.sin_port=htons(request->port);

if( writeModbusPackage(&addr,&modbus) == X_UDP_OK )
#ifdef _UNICODE
traceline(L"UDP write ip:%S port:%d command:0x%02X address:0x%04X content:%S\n",inet_ntoa(addr.sin_addr),request->port,devReq->command,devReq->address,(devReq->pDetail&&devReq->pDetail[0])?devReq->pDetail:"NULL");
#else
traceline("UDP write ip:%s port:%d command:0x%02X address:0x%04X content:%S\n",inet_ntoa(addr.sin_addr),request->port,devReq->command,devReq->address,(devReq->pDetail&&devReq->pDetail[0])?devReq->pDetail:"NULL");
#endif
else
traceline(TEXT("UDP write request failed.\n"));

delete request;
}
}
}

traceline(TEXT("udp worker thread aborted.\n"));
return 0;
}
Geoff08Zhang 2012-10-23
  • 打赏
  • 举报
回复
局域网上,如果本机发送的数据多而且快,会造成丢包,建议你在sendto后Sleep几毫秒,或者等待对方给你一个收到数据的确认后再接着发送.这里有使用UDP的Daytime程序,你也可以参考一下:
http://download.csdn.net/detail/geoff08zhang/4571358
Joseph-Growth 2012-10-23
  • 打赏
  • 举报
回复
服务器上都丢了,只能说缓冲区已满,或者是代码逻辑操作问题。或是多线程资源共享问题
吹雪 2012-10-23
  • 打赏
  • 举报
回复
但是,每次这样有规律的丢包的原因是什么呢?
顺带补充一下,用sniffer和iris时,我只监控了UDP协议,其他如ARP协议包被过滤了。
fujialin2011 2012-10-23
  • 打赏
  • 举报
回复
UDP是不可靠的,但是你可以自己根据需要实现你想要保证的那部分,正如楼上所说,你可以写重发来保证数据不丢失
youngwolf 2012-10-23
  • 打赏
  • 举报
回复
udp没有保证过不丢包啊,不要去追究丢包丢不合不合理(比如你可能觉得速度快了才会丢包,速度慢了就不丢,这些都是你的猜测)。既然选择了udp,又想保证数据成功发送,那就得必须有一套重发机制,虽然这套机制可能从来没运行过,也不能少。
曾在微软设计大赛中获奖。是一个局域网内的通讯工具,有文字聊天、语音聊天、文件传输、信使服务、邮件检测等主要功能。 本压缩文件内共含四部分内容: 第一:本说明文件; 第二:两个可执行文件(LanChat(NT).exe只能在NT下使用,因为其中含了网络访问检测的部分;LanChat(98).exe不含网络访问检测部分,故可以在98及NT两种环境中使用); 第三:源代码,都放在子目录“new_EMU_RMCS0.61(微软大赛)”下; 第四:有关软件使用说明的一个 hlp 文件; 本软件初衷是为了不在同一楼层却在同一个局域网内的两个用户联系方便而开发的,随着后来各种需要的增加,新增加了一些别的功能,整个软件的使用及外观稍微模仿了QQ的一些特点; 本软件用户之间的沟通不是通过服务器转发的方式来进行的,而是通过广播的方式宣告自己以及查询网上还有别的哪些用户,当找到了相应的用户后,就采用点对点通信的方式来进行文字数据的发送,数据基于UDP模式,不采用TCP是因为局域网内网络状况较好,一般不会,而且UDP方式简单而且快捷。目前,软件的系统数据发送和聊天文字等沟通数据的发送都在一个端口内,通过识别特定字符串的方式来分开做相应处理(比如所有的系统消息数据都是在串的前后添加lsm而构成,如“系统查询是否存在高版本程序”的命令为 lsmCheckVersionlsm); 进入时的帐户口令保存在注册表中,通过MD5算法加密,密钥串中含了“用户的帐户信息+特定字符串”的方式,为破解增加了一定的难度;在语音聊天里(即通过声卡和麦克风的语音聊天),通过动态huffman编码来压缩传送的语音数据,使得语音聊天的大量语音数据能够及时、准确的传送而不会造成网络拥塞,动态huffman编码已经调通,而且由于是动态压缩,故huffman编码采用的是一遍扫描而不是两遍扫描数据的方式,所以可以采取边压缩边传送的方式,而解压也可以边解压边播放,方便了语音的实现。数据的压缩比根据数据特点的不同而有差异,相对于文本文件和语音数据文件(语音数据一般都含大量相同的ASCII值的数据)来说一般压缩为原来大小的1/2、1/3都没有问题,不过由于时间等一些原因,在程序中还未能加上传送的语音数据先经过huffman编码这一过程,这点比较遗憾,不过即使不加也已经能够实现语音聊天而不会产生时延,如果加上压缩,相信互相的聊天通信对于网络的压力能够大大减轻,而且声音的采样频率也可以提高,使得声音的回放更加逼真; 另外,本程序还含邮件检测(通过RFC标准文件中规定的通信协议)、简单邮件发送、信使服务、定时提醒、定时关机等功能,其中,检测邮件、定时提醒、语音聊天等功能的实现是通过分别启动一个线程的方式,这样在使用这些功能时就不会影响到主程序的响应速度,而且相互之间也不会有影响; 本程序还初步试验了对于网络访问本机检测的相关函数,并添加了“网络访问检测报告”功能(LanChat(NT).exe就可实现此功能),不过由于程序重点不在此,故实现的东西只是一个初步的试验,实用性不大。

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧