Ff672dce2668c41236191f183e2ab35f
TCP糊涂窗口综合症

上一篇文章提到了滑动窗口,是用来控制流量的。接收端每次发送ACK包的时候带一个RWND的值,接收端据此调整SWND的大小。但实践中会出现每次RWND都很小,甚至为1。那就会出现发送端每次只发送1个字节的数据,但是还有40字节的头(20字节TCP头+20字节IP头),这就造成了网络利用率低下。这种现象被称作糊涂窗口综合症(Silly Window Syndrome, SWS, 貌似有人也称为小包风暴)。这里的“窗口”就是指滑动窗口,我个人对“糊涂”的理解是发送方和接收方都没有感知到窗口滑动的值非常小,而是选择继续按原有逻辑滑动窗口,也就是说它只负责可靠性和流量控制,并不关心小包造成的效率问题。

目录

  1. 成因
    1.1 发送端引起
    1.2 接收端引起
  2. 解决算法
    2.1 Nagle算法
    2.2 Cork算法
    2.3 Clark解决方法
    2.4 延迟Ack

1. 成因

糊涂窗口综合症可能由发送端或接收端或者两者同时引起的。当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有时,就会使应用进程间传送的报文段很小,特别是有效载荷很小,造成糊涂窗口综合症。

1.1 发送端引起

发送端如果产生数据的速度很慢(典型的有telnet应用),比如一次产生一个字节。那么底层TCP默认就每次发送只有一个字节负载的报文段。这就出现了糊涂窗口综合症状了。
为了避免这种情况,必须规定发送端的等到组装成一个大的报文才发送。这就牵扯到两个问题:1)等多久?2)组装成多大的包?
这两个问题将在接下来的Nagle算法中很好地解决。

1.2 接收端引起

与发送端产生数据速度很慢相对应的是接收端可能消耗数据的速度也很慢。那么RWND就会堆积,极端情况下每次ACK时的RWND值都是1个字节。
我们可以用一组数据实验来看。假设MSS现在是360个字节,接收端初始化RWND是360,那么发送端就可以发送一个完整大小的报文。但如果此时接收端负载很高,每次只能消化掉接收数据的1/3。又假设它在等待下一个报文来的RTT时间内又能消化掉额外的40字节。就会出现以前场景:

  1. SWND是360,发送端立马发送360字节的数据,使用完SWND。
  2. 接收端收到后回复ACK,但是它只能消耗掉120字节(1/3),那么ACK的Window字段就置为120。
  3. 发送端收到了前360字节的ACK,一看RWND只有120了,那就只能发送120字节的数据了。
  4. 在等待发送端发送下一个包的时候,接收端又消耗了40字节,那么现在TCP Buffer里剩余了200字节了(360-120-40=200)。这一次接收端可以消耗40字节(120/3=40),剩余的80字节又加到Buffer中,此时Buffer有280字节(200+80=280),那么接收窗口只有80了(360-280=80)。ACK的Window字段就置为80。
  5. 发送端发送80字节的报文。
  6. 接收端Buffer剩余240字节(280-40=240),又消耗27字节(80/3),Buffer此时就是293字节了(240+80-27=293)。那RWND就是67了(360-293=67)。

整个过程可以用图表示如下:

图片来自网络

图片来自网络

RWND被不断低减小,如此下去,肯定每次只能发送很小的包,极端情况下,如果接收端可能只能消耗一个字节,那就造成了SWS现象。

2. 解决算法

top Created with Sketch.