缓存的大小与TCP的滑动窗口到底有何关联?

(1)滑动窗口的大小与缓存大小肯定是有关的,但却不是一一对应的关系,更不会与缓存上限具有一一对应的关系。因此,网上很多资料介绍rmem_max等配置设置了滑动窗口的最大值,与我们tcpdump抓包时看到的win窗口值完全不一致,是讲得通的。下面我们来细探其分别在哪里。

读缓存的作用有2个:1、将无序的、落在接收滑动窗口内的TCP报文缓存起来;2、当有序的、可以供应用程序读取的报文出现时,由于应用程序的读取是延时的,所以会把待应用程序读取的报文也保存在读缓存中。所以,读缓存一分为二,一部分缓存无序报文,一部分缓存待延时读取的有序报文。这两部分缓存大小之和由于受制于同一个上限值,所以它们是会互相影响的,当应用程序读取速率过慢时,这块过大的应用缓存将会影响到套接字缓存,使接收滑动窗口缩小,从而通知连接的对端降低发送速度,避免无谓的网络传输。当应用程序长时间不读取数据,造成应用缓存将套接字缓存挤压到没空间,那么连接对端会收到接收窗口为0的通知,告诉对方:我现在消化不了更多的报文了。

反之,接收滑动窗口也是一直在变化的,我们用tcpdump抓三次握手的报文:


  1. 14:49:52.421674 IP houyi-vm02.dev.sd.aliyun.com.6400 > r14a02001.dg.tbsite.net.54073: S 2736789705:2736789705(0) ack 1609024383 win 5792 <mss 1460,sackOK,timestamp 2925954240 2940689794,nop,wscale 9> 

可以看到初始的接收窗口是5792,当然也远小于最大接收缓存(稍后介绍的tcp_rmem[1])。

这当然是有原因的,TCP协议需要考虑复杂的网络环境,所以使用了慢启动、拥塞窗口(参见 高性能网络编程2—-TCP消息的发送 ),建立连接时的初始窗口并不会按照接收缓存的最大值来初始化。这是因为,过大的初始窗口从宏观角度,对整个网络可能造成过载引发恶性循环,也就是考虑到链路上各环节的诸多路由器、交换机可能扛不住压力不断的丢包(特别是广域网),而微观的TCP连接的双方却只按照自己的读缓存上限作为接收窗口,这样双方的发送窗口(对方的接收窗口)越大就对网络产生越坏的影响。慢启动就是使初始窗口尽量的小,随着接收到对方的有效报文,确认了网络的有效传输能力后,才开始增大接收窗口。

不同的linux内核有着不同的初始窗口,我们以广为使用的linux2.6.18内核为例,在以太网里,MSS大小为1460,此时初始窗口大小为4倍的MSS,简单列下代码(*rcv_wnd即初始接收窗口):


  1. int init_cwnd = 4; 
  2.  if (mss > 1460*3) 
  3.   init_cwnd = 2; 
  4.  else if (mss > 1460) 
  5.   init_cwnd = 3; 
  6.  if (*rcv_wnd > init_cwnd*mss) 
  7.   *rcv_wnd = init_cwnd*mss; 

大家可能要问,为何上面的抓包上显示窗口其实是5792,并不是1460*4为5840呢?这是因为1460想表达的意义是:将1500字节的MTU去除了20字节的IP头、20字节的TCP头以后,一个最大报文能够承载的有效数据长度。但有些网络中,会在TCP的可选头部里,使用12字节作为时间戳使用,这样,有效数据就是MSS再减去12,初始窗口就是(1460-12)*4=5792,这与窗口想表达的含义是一致的,即:我能够处理的有效数据长度。

在linux3以后的版本中,初始窗口调整到了10个MSS大小,这主要来自于GOOGLE的建议。原因是这样的,接收窗口虽然常以指数方式来快速增加窗口大小(拥塞阀值以下是指数增长的,阀值以上进入拥塞避免阶段则为线性增长,而且,拥塞阀值自身在收到128以上数据报文时也有机会快速增加),若是传输视频这样的大数据,那么随着窗口增加到(接近)最大读缓存后,就会“开足马力”传输数据,但若是通常都是几十KB的网页,那么过小的初始窗口还没有增加到合适的窗口时,连接就结束了。这样相比较大的初始窗口,就使得用户需要更多的时间(RTT)才能传输完数据,体验不好。

那么这时大家可能有疑问,当窗口从初始窗口一路扩张到最大接收窗口时,最大接收窗口就是最大读缓存吗?

不是,因为必须分一部分缓存用于应用程序的延时报文读取。到底会分多少出来呢?这是可配的系统选项,如下:


  1. net.ipv4.tcp_adv_win_scale = 2 

这里的 tcp_adv_win_scale意味着,将要拿出1/(2^ tcp_adv_win_scale )缓存出来做应用缓存。即,默认 tcp_adv_win_scale配置为2时,就是拿出至少1/4的内存用于应用读缓存,那么,最大的接收滑动窗口的大小只能到达读缓存的3/4。

(2)最大读缓存到底应该设置到多少为合适呢?

当应用缓存所占的份额通过tcp_adv_win_scale配置确定后,读缓存的上限应当由最大的TCP接收窗口决定。初始窗口可能只有4个或者10个MSS,但在无丢包情形下随着报文的交互窗口就会增大,当窗口过大时,“过大”是什么意思呢?即,对于通讯的两台机器的内存而言不算大,但是对于整个网络负载来说过大了,就会对网络设备引发恶性循环,不断的因为繁忙的网络设备造成丢包。而窗口过小时,就无法充分的利用网络资源。所以,一般会以BDP来设置最大接收窗口(可计算出最大读缓存)。BDP叫做带宽时延积,也就是带宽与网络时延的乘积,例如若我们的带宽为2Gbps,时延为10ms,那么带宽时延积BDP则为2G/8*0.01=2.5MB,所以这样的网络中可以设最大接收窗口为2.5MB,这样最大读缓存可以设为4/3*2.5MB=3.3MB。

【声明】:芜湖站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

相关文章