UDP发包的延时问题
来源:互联网 发布:手机平面设计图纸软件 编辑:程序博客网 时间:2024/06/06 10:53
首先说明下问题描述,就是当我们在做数据转发时,一般的1对N发送数据,应为发送的时候,我们不可能确定得了这个数据是否是有效的,一般是通过心跳包来检测,但是心跳包不是实时的,这个时候如果在N个地址中出现了无效地址,就会造成延时….项目中好像不是这样的,客户端掉线后,没问题,但是客户端掉线后连接上服务器,就会出现卡顿的情况,目前还在查找中….但是想把自己这3天的学习与资料查找做一个总结.不足之处还望不吝批评指正。
测试demo的思路如下,客户端 - > 服务端 - > N个接收端. 然后N个服务端中出现部分掉线,如果客户端发送Msg的时间与最后接收端的时间相差超过10ms我们就认为造成了延时,并给出解决方案.
通过线程模拟接收端,我这里是开了30个+10个会掉线的线程
接收线程,
bool g_stop = false;Event g_event(false);void RecvThread(void* pData){ int nPort = *(int*)pData; DatagramSocket recvSocket; SocketAddress addr("172.16.11.228", nPort); recvSocket.bind(addr); recvSocket.setBlocking(false); Timespan span(1000); char szMsg[512] = { 0 }; while (!g_stop) { try { if (recvSocket.poll(span, Poco::Net::Socket::SELECT_READ)) { SocketAddress addrfrom; int nlen = recvSocket.receiveFrom(szMsg, 512, addrfrom); if (nlen > 0) { szMsg[nlen] = 0; DateTime dt; printf("ThreadID:%05d,RecvTime:%02d:%02d:%03d===Msg:%s\n", Thread::currentTid(), dt.minute(), dt.second(), dt.millisecond(), szMsg); } } } catch (Poco::Exception& e) { printf("Exception:%s\n", e.displayText()); } } recvSocket.close();}
发送线程
void SendThread(void* pdata){ DatagramSocket cSocket; SocketAddress addr("172.16.11.228", 33333); int nIndex = 0; cSocket.setSendTimeout(Timespan(1000)); cSocket.setReceiveTimeout(Timespan(1000)); try { cSocket.connect(addr); } catch (Poco::Exception& e) { printf("err:%d,%s\n", Thread::currentTid(), e.displayText().c_str()); return; } char szMsg[512] = { 0 }; g_event.wait(); while (nIndex < 1000) { try { DateTime dt; int nlen = sprintf_s(szMsg, "Recv ThreadID:%05d Time:%02d:%02d:%03d", Thread::currentTid(),dt.minute(),dt.second(),dt.millisecond() ); nlen = cSocket.sendTo(szMsg, 256, addr); SocketAddress recvaddr; } catch (Poco::Exception& e) { printf("ThreadID:%05d,SendRecvErr:%s\n", Thread::currentTid(), e.displayText().c_str()); ++nIndex; continue; } Sleep(4); ++nIndex; } cSocket.close();}
main函数
int _tmain(int argc, _TCHAR* argv[]){ g_event.reset(); Thread sendthread; sendthread.start(SendThread, NULL); Thread arrThread[30]; for (int i = 0; i < 30;++i ) { int nport = 60000 + i; arrThread[i].start(RecvThread, &nport); Sleep(10); } g_event.set(); getchar(); g_stop = true; getchar(); return 0;}
上述代码实现了..发送线程间隔4ms发送一个数据包,另外有30个线程负责接收服务器回显的数据包
发送线程,记录发送时间,然后与30个线程接收的数据进行对比,上述为了简单点,使用了poco开发库
服务端
之前使用了BSD套接字,也就是poco 封装底层的实现,但是没办法解决UDP延时的问题,再windows下不能设置socket选项,必须使用WSAIOCTL来这是,就自己封装了…看代码
#include "stdafx.h"#include "Poco/Net/Net.h"#include "Poco/Net/DatagramSocket.h"#include "Poco/Foundation.h"#include "Poco/Timespan.h"#include "Poco/DateTime.h"#include "Poco/Thread.h"#include <WinSock2.h>#include <vector>#include <Windows.h>using Poco::Thread;using std::vector;bool g_stopThrad = false;#pragma comment(lib,"ws2_32.lib")#define DEFAULTPORT 33333vector<SOCKADDR_IN> vecaddr;#define IOC_VENDOR 0x18000000 #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) void WorkThread(void* pdata){ int nret = -1; WSADATA wsadata; SOCKET serversocket = INVALID_SOCKET; serversocket = socket(AF_INET, SOCK_DGRAM, 0); SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_addr.S_un.S_addr = INADDR_ANY; nret = bind(serversocket,(SOCKADDR*)&addr, sizeof(addr)); ULONG block = 1; ioctlsocket(serversocket, FIONBIO, &block); DWORD dwBytesReturned = 0; BOOL bNewBehavior = FALSE; DWORD dwstatus = 0; ***dwstatus = WSAIoctl(serversocket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL);*** fd_set fdread; TIMEVAL tm; FD_ZERO(&fdread); FD_SET(serversocket, &fdread); tm.tv_sec = 0; tm.tv_usec = 500000; char szMsg[2048] = { 0 }; while (!g_stopThrad) { nret = select(0,&fdread, NULL, NULL, &tm); if (nret <= 0 ) { printf("err:%d\n", WSAGetLastError()); FD_ZERO(&fdread); FD_SET(serversocket, &fdread); continue; } nret = recvfrom(serversocket, szMsg, 2048, 0, NULL, NULL); if (nret > 0 ) { szMsg[nret] = 0; Poco::DateTime tm; printf("Time:%d:%d:%d---info:%s\n", tm.minute(), tm.second(), tm.millisecond(), szMsg); for each (SOCKADDR_IN addr in vecaddr) { sendto(serversocket, szMsg, nret,0, (SOCKADDR*)&addr, sizeof(SOCKADDR)); } } } closesocket(serversocket);}
服务端main函数
列表内容====int _tmain(int argc, _TCHAR* argv[]){ for (int i = 0; i < 40; ++i) { int nPort = 60000 + i; SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(nPort); addr.sin_addr.S_un.S_addr = inet_addr("172.16.11.228"); vecaddr.push_back(addr); } Thread workThread; workThread.start(WorkThread); getchar(); g_stopThrad = true; getchar(); return 0;}
这是正确的解决方案,错误的解决方案是不使用上面加粗和斜线的那一句,原理是windows下当我们向一个不存在的地址sendto数据后,会有一个icmp包的回显,造成select时间为可读,进而会造成select阻塞或者超时,也就引起了…udp接收线程与发送线程的时间相差很大的问题…具体理论链接参考博客尾部
直接上结果…
不设置Socket的情况,如下图…
我们可以看到发送的时间与接收到的时间相差了40ms左右,这个再实时音频传输中是不被允许的,因为标准的MP3音频是26ms…更别说其他的自定义的了..所以会有卡顿的现象出现.
设置socket的情况,如下图
基本是没有延时的,同样的测试步骤,我是这样测试的,其实接收线程就相当于远端服务器,我另外在开一个差不多类似的客户端代码,但是只是接收100条Msg就退出,相当于模拟了无效地址..测试了10几次,最大的延时是2ms内,在我们允许的范围内,但是第一张图,基本上2-3次就会出现大量的延时..根据网络上的资料,udp socket一次收发耗时在2us左右,40*2us = 800us 接近1ms了..所以基本上符合预期了.当然论证不足或者有疏漏出请大家批评指针,一起进步
//参考链接
网络参考链接,还是比较中肯
- UDP发包的延时问题
- 发包的前夜问题
- (ZT)udp发包收包的例子
- UDP 发包程序
- UDP发包收包
- udp 137端口发包
- udp发包大小
- udp发包流程
- udp发包大小选择
- 转 java UDP发包
- UDP 广播发包失败
- UDP协议发包的使用(DatagramSocket、DatagramPacket)
- 关于winpcap发包速度低的问题
- 解决iperf发包高丢包率的问题
- 关于UDP发包长度及分包问题限制问题(笔记)
- 流量整形技术是否能解决UDP TS 均匀发包问题
- 用实际程序来测试udp sendto函数的最大发包大小------为什么是65507?
- 【转帖】Ndis中间层驱动自己发包的实现(经典问题)
- 机器学习的决策树介绍
- Android消息处理Handler与Message
- 155. Min Stack
- postman设置环境变量,实现一套接口根据选择的环境去请求不同的url
- HDU2571:命运(DP)
- UDP发包的延时问题
- 关于加入NDK编译环境的方法以及如何加入PIE模块的编译选项
- 使用arcgis for js 4.x api加载地图和服务
- Css3 动画效果
- Linux学习总结
- Web前端开发最佳实践(5):正确闭合HTML标签,停止使用不标准的标签和属性
- 新人到,坑里继续跳……
- CSS3有哪些新特性?
- 学生信息系统心路历程