来源:互联网 发布:北京淘宝大学在哪 编辑:程序博客网 时间:2024/05/27 20:06

 

我不是什么大牛,稍懂一点。MTU的大小一般都设为1500,除去28个字节的头外就是数据包的长度。UPD也好TCP包也好,最终从网卡上出去,以太网卡一次发送的大小为1500,如果超过了这个数就要分包。你用WINDOWS的SOCKET发送,它有一个发送缓冲,只要你不超过这个都可以发。在局域网网肯定没问题,但是如果你的
MTU是1500而路由的是1400,那么你的数据就将发送不出去。呵呵所以一般建议不超过1464,这个数是一些老版本以太网协议的系统的MTU1492-28得到的。当然如果设你自己的MTU为最小,那么你依然可以发尽可能大的包,如你所想WINDOWS给你分包。

 

 

 

UDP包大小同TCP一样,只受缓冲限制,超过MTU会被分片,分片同样能正确的传到目的主机,但由于UDP没有差错控制,不能重传丢失的分片,所以如果UDP包太大,分片过多,只要丢失一片,整个包就会被丢弃,所以现实中应该使用不分片的大小传送UDP包

 

 

个人觉得有2个可能的原因,不知道是否正确

1。是否设置了不允许分段?抓包的时候显示DF=1,即为不允许分段,这样当数据包大于1500时会出问题。

2。是否禁用了PMTU?

测试:

可以用ping -f -l  命令测试。-f是设置不允许分段,-l可以设置数据包大小。

 

 

 

什么会导致udp丢包呢,我这里列举了如下几点原因:

1.调用recv方法接收端收到数据后,处理数据花了一些时间,处理完后再次调用recv方法,在这二次调用间隔里,发过来的包可能丢失。对于这种情况可以修改接收端,将包接收后存入一个缓冲区,然后迅速返回继续recv。
2.发送的包巨大丢包。虽然send方法会帮你做大包切割成小包发送的事情,但包太大也不行。例如超过30K的一个udp包,不切割直接通过send方法发送也会导致这个包丢失。这种情况需要切割成小包再逐个send。
3.发送的包较大,超过mtu size数倍,几个大的udp包可能会超过接收者的缓冲,导致丢包。这种情况可以设置socket接收缓冲。以前遇到过这种问题,我把接收缓冲设置成64K就解决了。
int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
4.发送的包频率太快,虽然每个包的大小都小于mtu size 但是频率太快,例如40多个mut size的包连续发送中间不sleep,也有可能导致丢包。这种情况也有时可以通过设置socket接收缓冲解决,但有时解决不了。
5.发送的广播包或组播包在windws和linux下都接收正常,而arm上接收出现丢包。这个还不好解决,我的解决方法是大包切割成大小为1448的小包发送,每个包之间sleep 1毫秒,虽然笨,但有效。我这里mtu size为1500字节,减去udp包头8个字节,减去传输层几十个字节,实际数据位1448字节。
除此之外还可以试试设置arm操作系统缓冲:
//设置mtu size 1500最大
ifconfig eth0 mtu 1500
//查看接收缓冲最大和默认大小。
sysctl -A | grep rmem
//设置接收缓冲的最大大小
sysctl -w net.core.rmem_max=1048576
sysctl -w net.core.rmem_default=1048576
sysctl -w net.ipv4.udp_mem=1048576
sysctl -w net.ipv4.udp_rmem_min=1048576
6,局域网内不丢包,公网上丢包。这个问题我也是通过切割小包并sleep发送解决的。如果流量太大,这个办法也不灵了。


总之udp丢包总是会有的,如果出现了用我的方法解决不了,还有这个几个方法: 要么减小流量,要么换tcp协议传输,要么做丢包重传的工作。

 

 

 

 

TCP和UDP发送数据包的大小问题

以下是在网上搜到的一段描述:
用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535-20-8=65507字节,其中20字节为IP包头长度,8字节为UDP包头长度。用sendto函数发送数据时,如果指的的数据长度大于该值,则函数会返回错误。

用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓冲区的大小),这是指在
用send函数时,数据长度参数不受限制。而实际上,所指定的这段数据并不一定会一次性发送出去,如果这段数据比较长,可能会被分段发送,如果比较短,可能会等待和下一次数据一起发送。
我在测试的时候,发现长度一般会被切成16384(16K)或49152(48K),不知道这两个值有什么意义。
比如在send()中设定发送数据的长度为100000,在接收端用recv()函数接收时,接收到的数据长度如下表:
start recvLen: 16384 nu: 0 type: 0
normal recvLen: 16384 nu: 0 type: 0
normal recvLen: 67232 nu: 0 type: 0
-------------------
normal recvLen: 49152 nu: 1 type: 1
normal recvLen: 49152 nu: 0 type: 0
normal recvLen: 1696 nu: -1208847736 type: -1207635502
-------------------
normal recvLen: 49152 nu: 2 type: 1
normal recvLen: 49152 nu: 0 type: 0
normal recvLen: 1696 nu: -1208847736 type: -1207635502
-------------------
从表中可以看出每次发送的100000byte的数据被拆成了3段,这3段长度加起来正好是100000。
经过测试得出如果将send()函数参数中的数据长度设为16384(16K),每次用recv()接收到的长度也为16384,而大于或小于这个值,都会被拆分或合并。

本人的测试:
我暂且不知道是在Linux还是Windows上面做的实验。我是在Linux上做的实验,用UDP发视频数据没看到大的丢失(可能由于UDP每次支持的数据报比较大的原因),但是用TCP受到MTU(最大为1500)的限制,如果超过此值,则必须是分包发送的。于是,UDP的发送与接收我没做任何处理,只是有一些丢包(可能与此有关)。但是TCP的发送端也没做任何处理,由于TCP是可靠的连接的,不存在丢包原因,只不过把数据包分成几次发送而已,于是我在接收端参考如下代码做处理:

socket s;
char* buf=new char[100000];
unsigned int totlelen =100000 ;
unsigned int sendlen;
do
{
sendlen = send(s,buf,totlelen,0);
totlelen -=sendlen ;
}while (totlelen > 0);
接收数据同发送数据类似。


另外,上面的的解答最好还需加入返回处理,
do
{
sendlen = send(s,buf,totlelen,0);
if(sendlen == SOCKETERR)
{
int err = WSAGetLastErr();
if()
{
}
}
totlelen -=sendlen ;
}while (totlelen > 0);

 

 

 

 http://www.cnweblog.com/fly2700/archive/2011/09/19/317825.html

http://www.360doc.com/content/11/1125/17/7503466_167339511.shtml

原创粉丝点击