TCP 和 UDP 套接字缓冲区

1、MTU(Maximum Transmission Unit)

最大传输单元,在数据链路层中,往往规定了MTU大小,IP层的数据包通过数据链路层如果大于MTU,将被分片,到达接收端IP层后再被重组。以太网的MTU为1500字节。

2、MSS(Maximum Segment Size)

最大报文段,是TCP协议的一个选项。MSS选项用于在TCP建立连接时,收发双方协商一个TCP报文段所能承载的最大数据长度。MSS选项只在初始化连接请求(SYN=1)的报文段中使用。选择合适的MSS很重要。如果MSS小了,网络利用率低。如果MSS大了,由于在网络层需要分片,也会影响网络性能。一般MSS的长度为MTU(1500)-IP首部(20)-TCP首部(20)=1460字节。

3、TCP的缓冲区

TCP 和 UDP 套接字缓冲区

如上图所示,每一个TCP套接字都有一个发送缓冲区,可以使用SO_SNDBUF套接字选项来更改缓冲区大小。我们调用send发送数据的时候(默认阻塞模式),如果缓冲区没满,调用直接返回。但是这仅仅表明数据被复制到缓冲区中,并不表明对端接收到数据。系统内核在IP层发送数据的时候,并不是按照我们调用send接口发送的数据包大小来进行发送,即使我们调用send发送的数据大小小于1460字节(MTU-TCP首部-IP首部)。因为我们调用send接口实际是将数据复制到缓冲区中,而内核基本上是按照最大MSS大小(1460字节)从缓冲区中取数据发送出去,当缓冲区中数据小于MSS,则将剩余数据全部发送出去。TCP的发送缓冲区必须为已发送的数据保留一个副本,直到它被对端确认为止,才能从缓冲区中删掉已确认的数据。

TCP接收缓冲区,可以通过SO_RCVBUF套接字选项来更改。接收缓冲区被TCP用来保存接收到的数据,直到应用程序来读取。对于TCP来说,接收缓冲区中可用空间的大小限定了TCP通告对端的窗口大小。TCP套接字的接收缓冲区不能溢出,所以发送端不能发送超过接收端通知的窗口大小,否则在接收端将丢弃数据包。

4、UDP的缓冲区

                                          TCP 和 UDP 套接字缓冲区

以虚线框展示套接字发送缓冲区,因为它实际上并不存在,UDP有发送缓冲区大小,也可以通过SO_SNDBUF套接字选项更改它,不过它不同于TCP的发送缓冲区大小,它仅仅是可以写到该套接字的UDP数据报的大小上限。如果一个应用程序写一个大于套接字发送缓冲区大小的数据报,内核将返回一个EMSGSIZE错误。UDP缓冲区中数据被发送完之后,该数据就被删除了。我们调用sendto发送数据的时候,内核在收到用户的数据报,仅仅给数据包加上8字节的首部构成UDP数据报,然后就传给IP层(写一个UDP套接字的write调用成功返回表示所写的数据报或其所有分段已被加入数据链路层的输出队列中。如果该队列没有足够的空间存放该数据报或它的某个片段,内核通常会返回一个ENOBUFS错误给它的应用程序。)。如果数据报大小小于MTU,则直接发送给对端;如果大于MTU,则会被分片。所以通过UDP协议发送数据报,应该考虑发送的数据包小于MTU-8(UDP首部)-20(IP首部),这样在通过IP层就不用分片,丢包率将比分片处理的情况小很多。

UDP的接收缓冲区,同样通过SO_RCVBUF套接字选项更改它。当接收到的数据报装不进接收缓冲区,该数据报就被丢弃。

5、TCP缓冲区的调用顺序

当设置TCP套接字缓冲区大小时,函数的调用顺序很重要。因为TCP的窗口规模选项是在建立连接时用SYN分节与对端互换得到的。对于客户端,这意味着缓冲区选项必须在调用connect之前设置。对于服务端,这意味着该选项必须在调用listen之前给监听套接字设置,已连接套接字的缓冲区大小总是从监听套接字继承而来。