大宇私人领地伊甸园丨宁愿做了后悔,也不要错过后悔[www.mrdayu.com]
注册

24小时联系邮箱:[email protected]

所有文章
大宇私人伊甸园: 首页 > 所有文章 > UDP各种疑难杂症

UDP各种疑难杂症

发布时间:2017/12/09 所有文章 网络协议 标签udp阅读:398

引:最近一个项目,实时性和准确性要求极高,单位在选购材料的时候却选择了UDP协议的硬件!

UDP的传输特点:面向报文

面向报文的传输方式决定了UDP的数据发送方式是一份一份的,也就是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。

那么UDP的报文大小由哪些影响因素呢?

UDP数据包的理论长度是多少,合适的UDP数据包应该是多少呢?

UDP报文大小的影响因素,主要有以下3个

  • UDP协议本身,UDP协议中有16位的UDP报文长度,那么UDP报文长度不能超过2^16=65536.
  • 以太网(Ethernet)数据帧的长度,数据链路层的MTU(最大传输单元)。
  • socket的UDP发送缓存区大小

 UDP数据包最大长度(最好不要使用最大单位)
根据UDP协议,从UDP数据包的包头可以看出,UDP的最大包长度是2^16-1的个字节。

由于UDP包头占有8个字节,而在IP层进行封装后的IP包头占去20字节,所以这个是UDP数据包的最大理论长度是2^16 – 1 – 8 – 20 = 65507字节。

如果发送的数据包超过65507字节,send或sendto函数会错误码1(Operation not permitted, Message too long),一个数据包能否发送65507字节,还和UDP发送缓冲区大小相关。

linux下UDP发送缓冲区大小为:cat /proc/sys/net/core/wmem_default

windows的我不太清楚。。。

如果发送缓冲区小于N个字节,在发送一个数据包为N+1字节的时候,send或sendto函数会错误码1(Operation not permitted, No buffer space available)。当然N+任何右极限的值都会有问题,请举一反三。

UDP数据包理想长度
理论上UDP报文最大长度是65507字节,实际上发送这么大的数据包效果最好吗?

我们知道UDP是不可靠的传输协议,为了减少UDP包丢失的风险,我们最好能控制UDP包在下层协议的传输过程中不要被切割。

相信大家都知道MTU这个概念。 MTU最大传输单元,这个最大传输单元实际上和链路层协议有着密切的关系。

EthernetII帧的结构DMAC+SMAC+Type+Data+CRC由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64字节,最大不能超过1518字节,对于小于或者大于这个限制的以太网帧我们都可以视之为错误的数据帧,一般的以太网转发设备会丢弃这些数据帧。

由于以太网EthernetII最大的数据帧是1518字节,除去以太网帧的帧头(DMAC目的MAC地址48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和帧尾CRC校验部分4Bytes那么剩下承载上层协议的地方也就是Data域最大就只能有1500字节这个值我们就把它称之为MTU。
在下层数据链路层最大传输单元是1500字节的情况下,要想IP层不分包,那么UDP数据包的最大大小应该是1500字节 – IP头(20字节) – UDP头(8字节) = 1472字节。

不过鉴于Internet上的标准MTU值为576字节,所以建议在进行Internet的UDP编程时,最好将UDP的数据长度控制在 (576-8-20)548字节以内。

UDP数据包的发送和接收问题:

(1) UDP的通信有界性
在阻塞模式下,UDP的通信是以数据包作为界限的,即使server端的缓冲区再大也要按照client发包的次数来多次接收数据包,server/client只能一次一次的接收,client/server发送多少次,server/client就需接收多少次,即客户端分几次发送过来,服务端就必须按几次接收。

(2) UDP数据包的无序性和非可靠性
client依次发送1、2、3三个UDP数据包,server端先后调用3次接收函数。

可能会依次收到3、2、1次序的数据包,收包可能是1、2、3的任意排列组合,也可能丢失一个或多个数据包。

(3) UDP数据包的接收
client发送两次UDP数据,第一次 500字节,第二次300字节,server端阻塞模式下接包。

第一次recvfrom( 1000 ),收到是 1000,还是500,还是300,还是其他?
由于UDP通信的有界性,接收到只能是500或300,又由于UDP的无序性和非可靠性,接收到可能是300,也可能是500,
也可能一直阻塞在recvfrom调用上,直到超时返回(也就是什么也收不到)。

在假定数据包是不丢失并且是按照发送顺序按序到达的情况下,server端阻塞模式下接包,先后三次调用:recvfrom( 200),recvfrom( 1000),recvfrom( 1000),接收情况如何呢?

由于UDP通信的有界性,第一次recvfrom( 200)将接收第一个500字节的数据包,但是因为用户空间buf只有200字节,于是只会返回前面200字节,剩下300字节将丢弃。第二次recvfrom( 1000)将返回300字节,第三次recvfrom( 1000)将会阻,也可能一直阻塞在recvfrom调用上,直到超时返回(也就是什么也收不到)。

(4) UDP包分片问题
如果MTU是1500,Client发送一个8000字节大小的UDP包,那么Server端阻塞模式下接包,在不丢包的情况下,recvfrom(9000)是收到1500,还是8000。如果某个IP分片丢失了,recvfrom(9000),又返回什么呢?
根据UDP通信的有界性,在buf足够大的情况下,接收到的一定是一个完整的数据包,UDP数据在下层的分片和组片问题由IP层来处理,提交到UDP传输层一定是一个完整的UDP包,那么recvfrom(9000)将返回8000。

如果某个IP分片丢失,udp里有个CRC检验,如果包不完整就会丢弃,也不会通知是否接收成功,所以UDP是不可靠的传输协议,那么recvfrom(9000)将阻塞。

UDP丢包问题:

在不考虑UDP下层IP层的分片丢失,CRC检验包不完整的情况下,造成UDP丢包的因素有哪些呢?

[1] UDP socket缓冲区满造成的UDP丢包
通过 cat /proc/sys/net/core/rmem_default 和cat /proc/sys/net/core/rmem_max可以查看socket缓冲区的缺省值和最大值。如果socket缓冲区满了,应用程序没来得及处理在缓冲区中的UDP包,那么后续来的UDP包会被内核丢弃,造成丢包。在socket缓冲区满造成丢包的情况下,可以通过增大缓冲区的方法来缓解UDP丢包问题。但是,如果服务已经过载了,简单的增大缓冲区并不能解决问题,反而会造成滚雪球效应,造成请求全部超时,服务不可用。

[2] UDP socket缓冲区过小造成的UDP丢包
如果Client发送的UDP报文很大,而socket缓冲区过小无法容下该UDP报文,那么该报文就会丢失。

[3] ARP缓存过期导致UDP丢包
ARP的缓存时间约10分钟,APR缓存列表没有对方的MAC地址或缓存过期的时候,会发送ARP请求获取MAC地址,在没有获取到MAC地址之前,用户发送出去的UDP数据包会被内核缓存到arp_queue这个队列中,默认最多缓存3个包,多余的UDP包会被丢弃。被丢弃的UDP包可以从/proc/net/stat/arp_cache的最后一列的unresolved_discards看到。当然我们可以通过echo 30 > /proc/sys/net/ipv4/neigh/eth1/unres_qlen来增大可以缓存的UDP包。
UDP的丢包信息可以从cat /proc/net/udp 的最后一列drops中得到,而倒数第四列inode是丢失UDP数据包的socket的全局唯一的虚拟i节点号,可以通过这个inode号结合lsof(lsof -P -n | grep 25445445)来查到具体的进程。

UDP真的比TCP要高效吗?你的良心不会痛吗?要不要再出个专题,来讲讲我的看法?

 

The bottom line, for me, is simple.

Let’s hope it doesn’t take 500 years for a discipline of testing to becomes the standard for software developers.

From:dayu

您好!请登录

合作网站快捷登录:
点击取消回复

已有0评论

    大宇博客

    点击这里给我发消息点击这里给我发消息