糊涂窗口综合征
糊涂窗口综合征(Silly Window syndrome SWS)是一个发送端与接收端处理包不一致 导致出现接收端的通告窗口过小 再导致发送端窗口过小 从而导致产生大量的小数据包 这样的小数据包中头部占比大 导致传输效率低下 这样的行为就叫做糊涂窗口综合征
在网上看到一个例子可以很好的说明这个过程
假设接收方的缓冲区已经满了 而接受端程序每次只能处理很少 假设是1个字节 即接收端维护的接收窗口每次大小都只有一 倘若没有避免SWS的方法 从接收端返回的报文上的通告窗口大小只有1 那么发送方发送窗口也只能发送一个字节 那么在数据包中以最小的包来估计 IP20个字节 TCP20个字节 那么这个数据包大小为41 可以看出利用率及其低下 所以我们必须要避免这种情况
避免SWS
因为发送端和接收端都可能出现SWS 所以分别从两端进行避免
发送端
- 到达MSS个字节的报文段可以发送(Nalge算法 防止网络中出现大量小数据包)
- 发送报文达到通告窗口的最大值的一半时发送(针对于窗口较小的情况)
接收端
- 等待窗口增至一个MSS
- 等待窗口增至接收端缓冲区一半空闲
取两者之间最小值时再发送 否则发送窗口大小为零
延迟确认机制
在接收端收到数据时先不进行回复确认 接收端在确认收到的报文段之前一直等待 直到缓存有足够的空间为止 这样可以使得发送端在这期间窗口不进行滑动 这样就避免了SWS,而且还减少了网络中的数据包 其中延迟确认机制的超时时间应小于500ms 实践中最大取200ms(延迟确认机制和Nagle算法在一些特殊情况下可能造成死锁 Nagle算法在同一时刻只传递一个包 即遵循停等规程(stop-and-wait) 那么有可能接收端Nagle等待ACK 发送端延迟确认 即造成死锁 造成无谓的时延(Nagle因为停等规程本来时延就高于一般无Nagle的传输)
零窗口
既然接收端SWS避免会防止发送小的数据包 那么在接收端有数据但不足已发送时会发生什么呢?会出现零窗口的情况 即接收端回复的ACK中的窗口选项为0,因为流量控制是基于通告窗口的 窗口为零的时候发送端窗口为零 不再发送数据。当接收端程序处理完成后的数据到达SWS的时候 从零窗口中恢复 发送一个Update Window,因为窗口更新ACK中一般不包含数据 其不消耗序列号 所以是不可靠的,这就出现了一种问题 如果这个窗口更新丢失那么就会出现这样一种情况:接收端缓冲区空闲等待发送端数据 发送端等待接收端的窗口更新 即出现了死锁
其实这种情况解决的方法也很简单 即发送端持续性发送窗口探测(window probe) 因为窗口探测中包含一个字节的数据 占序列号 所以是可靠的 一定会得到一个窗口更新 其后的机制类似于karn算法
在接收端其实有时会出现一个问题 即SWS避免与避免窗口收缩的优先级的问题 可以想这样一种情况 即发送端的窗口大小假设为2000 发送端一次发送了1600 即接收端在接收到后只剩400了,这个时候难道会在回复的ACK中设置通告窗口为400吗 这不就违背了SWS避免吗 答案是会的 因为如果回复窗口大小为0就会使得发送端窗口收缩(shrink) 即滑动窗口右端左移动 而其中有可能有已发送未ACK的报文段,所以在避免窗口收缩和SWS避免间前者优先级更高
参考
TCP/IP详解 卷一
https://blog.csdn.net/qq_35733751/article/details/80224079
https://www.cnblogs.com/zhaoyl/archive/2012/09/20/2695799.html