套接字中缓冲区

时间:2021-11-12 11:08:53

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

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

4、UDP的缓冲区

UDP也有发送缓冲区大小,也可以通过SO_SNDBUF套接字选项更改它,不过它不同于TCP的发送缓冲区大小,它仅仅是可以写到该套接字的UDP数据报的大小上限。如果一个应用程序写一个大于套接字发送缓冲区大小的数据报,内核将返回一个EMSGSIZE错误。UDP缓冲区中数据被发送完之后,该数据就被删除了。我们调用sendto发送数据的时候,内核在收到用户的数据报,仅仅给数据包加上8字节的首部构成UDP数据报,然后就传给IP层。如果数据报大小小于MTU,则直接发送给对端;如果大于MTU,则会被分片。所以通过UDP协议发送数据报,应该考虑发送的数据包小于MTU-8(UDP首部)-20(IP首部),这样在通过IP层就不用分片,丢包率将比分片处理的情况小很多。

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

5、TCP缓冲区的调用顺序

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

6、TCP套接字缓冲区的性能

在设置套接字缓冲区大小时需要考虑性能问题。下图展示了两个端点间容量为8个分节的一个TCP连接(称其为管道)。

套接字中缓冲区

我们在顶部给出4个数据分节,在底部给出4个ACK。即使管道中只有4个数据分节,客户端也必须有至少8个分节容量的发送缓冲区,因为客户端TCP必须为每个分节保留一个副本,直到接收到来自服务器的相应ACK。这里涉及管道容量的概念,称为“带宽-延迟积(bandwidth-delay product)”,它通过将带宽(bit/s)和RTT(s)相乘,再将结果由位转换为字节计算得到。其中,RTT可以使用ping程序测得。例如,客户端到服务端带宽为100Mb/s,客户端到服务端RTT为1ms,则带宽-延迟积为100*1/8为12.5KB。当套接字缓冲区大小小于该值,管道将不会处于满状态,性能达不到最高,所以缓冲区一般设置略大于带宽-延迟积。当带宽变大或者RTT变大,套接字缓冲区也需要增长。

 

注:以上参考《UNIX网络编辑卷1》2.11和7.5.8章节