UDP接收端缓冲区和丢包问题

实习项目需要用Winsock内核模式驱动提供的sockets方法,,这个驱动负责连接和缓冲管理,对应用程序提供socket风格的编程接口。

大概的流程是发送端将一幅图像分成多个包进行发送,接收端接收包整合成图像并显示。

通过测试发现,发送端是相机,采用udp协议,发送的图像数据包有3428个,并且通过wireshark抓取到全部的包,但是接收端只能接收到3000个包左右,出现了丢包现象。

分析UDP丢包的原因:

1)调用recvfrom方法接收到数据后,处理数据花费时间太长,再次调用recvfrom,两次调用间隔里,发过来的包可能丢失。

通过将接收到数据存入一个缓冲区,并迅速返回继续recvfrom,排除了这个原因

2)通过数据包的id域观察发现,接收的情况:接收到一些包之后丢失一些包,重复如此。分析原因,可能是因为连续多个UDP包超过了udp的接收端缓冲区(UDP包过大或者包频率过快),导致丢包。于是将接收缓冲设置为5M,问题就解决了。

int nRecvBuf = 5 * 1024 * 1024;      //设置成5M

Setsockopt(s,SOL_SOCKET, SO_RCVBUF, (const char *)&nRecvBuf,sizeof(nRecvBuf));int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

sockfd: 标识一个套接字的描述字

level: 选项定义的层次:支持SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP,和IPPROTO_IPV6

optname:需设置得选项 SO_RCVBUF(接收缓冲区),SO_SNDBUF(发送缓冲区)

optval:指针,指向存放选项待设置的新值的缓冲区

optlen:optval的大小

下面的内容来自:

概念:

MTU :链路层上数据帧中数据的最大值,即IP数据报的整个值。数据进入协议栈的封装过程。

MSS:TCP报文段中数据的最大值----MSS选项只能出现在SYN报文中。

TCP输出:

UDP接收端缓冲区和丢包问题

每个TCP套接口都有一个发送缓冲区,我们可以用SO_SNDBUF套接口选项来改变这个缓冲区的大小。当应用程序调用write时,内核从应用进程的缓冲区中拷贝所有数据到套接口的发送缓冲区。如果套接口发送缓冲区容不下应用程序所有的程序(或者应用程序的缓冲区大于套接口发送缓冲区,或者是套接口发送缓冲区还有其他数据),应用进程将被挂起,这里假设write是阻塞的。内核将不从write系统调用返回,直到将应用进程缓冲区的所有数据都拷贝到套接口发送缓冲区。因此从写一个TCP套接口的write调用成功返回仅仅代表我们重新使用应用进程的缓冲区。它并不告诉我们对端TCP或者应用进程已经接收到数据。

UDP输出:

UDP接收端缓冲区和丢包问题

这一次我们展示的套接口发送缓冲区用虚框表示,因为它并不存在。UDP套接口有发送缓冲区大小(SO_SNDBUF修改),不过它仅仅是写到套接口的UDP数据报的大小上限。如果一个应用程序写一个大于套接口发送缓冲区大小的数据报,内核将返回EMSGSIZE错误。既然UDP不可靠,它不必保存应用程序的数据拷贝,因此无需真正的发送缓冲区(应用进程的数据在沿协议栈往下传递,以某种形式拷贝到内核缓冲区,然而数据链路层在送出数据之后将丢弃该拷贝)。

根据上图可以发现,UDP没有MSS的概念,如果某个UDP应用进程发送大数据,那么它比TCP应用程序更容易分片。从UDP套接口write成功返回仅仅表示用户写入的数据报或者所有片段已经加入到数据链路层的输出队列。如果该队列没有足够的空间存放该数据包或者它的某个片段,内核通常返回给应用进程一个ENOBUFS错误。

TCP和UDP都拥有套接口接收缓冲区。TCP套接口接收缓冲区不可能溢出,因为TCP具有流量控制,然而对于TCP来说,当接收到的数据报装不进套接口接收缓冲区时,该数据报就丢弃。UDP是没有流量控制的:较快的发送端可以很容易淹没较慢的接收端,导致接收端的UDP丢弃数据报。

UNIX网络编程卷1:套接字联网API(第3版) 中文高清带完整书签 PDF  下载见

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/b474f484189cd036cdaba820349ff343.html