- Flow Control

- 接收端在给发送端回 ACK 中会汇报自己的 AdvertisedWindow = MaxRcvBuffer – LastByteRcvd – 1;
- 而发送方会根据这个窗口来控制发送数据的大小,以保证接收方可以处理。
Example

- 1: 已收到 ack 确认的数据。
- 2: 发还没收到 ack 的。
- 3: 在窗口中还没有发出的(接收方还有空间)。
- 4: 窗口以外的数据(接收方没空间)


On Zero Window
如果发送端不发数据了,接收方一会儿 Window size 可用了,怎么通知发送端呢?
TCP 使用了 Zero Window Probe 技术,缩写为 ZWP,也就是说,发送端在窗口变成 0 后,会发 ZWP 的包给接收方,让接收方来 ack 他的 Window 尺寸,一般这个值会设置成 3 次,间隔大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后还是 0 的话,有的 TCP 实现就会发 RST 把链接断了。
Silly Window Syndrome
如果我们的接收方太忙了,来不及取走 Receive Windows 里的数据,那么,就会导致发送方越来越小。到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的 window,而我们的发送方会义无反顾地发送这几个字节。
要知道,我们的 TCP+IP 头有 40 个字节,为了几个字节,要达上这么大的开销,这太不经济了。
另外,你需要知道网络上有个 MTU,对于以太网来说,MTU 是 1500 字节,除去 TCP+IP 头的 40 个字节,真正的数据传输可以有 1460,这就是所谓的 MSS(Max Segment Size)注意,TCP 的 RFC 定义这个 MSS 的默认值是 536,这是因为 RFC 791 里说了任何一个 IP 设备都得最少接收 576 尺寸的大小(实际上来说 576 是拨号的网络的 MTU,而 576 减去 IP 头的 20 个字节就是 536)。
如果你的网络包可以塞满 MTU,那么你可以用满整个带宽,如果不能,那么你就会浪费带宽。(大于 MTU 的包有两种结局,一种是直接被丢了,另一种是会被重新分块打包发送)
- 如果这个问题是由 Receiver 端引起的,那么就会使用 David D Clark’s 方案。在 receiver 端,如果收到的数据导致 window size 小于某个值,可以直接 ack(0) 回 sender,这样就把 window 给关闭了,也阻止了 sender 再发数据过来,等到 receiver 端处理了一些数据后 windows size 大于等于了 MSS,或者,receiver buffer 有一半为空,就可以把 window 打开让 send 发送数据过来。
- 如果这个问题是由 Sender 端引起的,那么就会使用著名的 Nagle’s algorithm。这个算法的思路也是延时处理,他有两个主要的条件:1)要等到 Window Size>=MSS 或是 Data Size >=MSS,2)收到之前发送数据的 ack 回包,他才会发数据,否则就是在攒数据。
- Nagle Algorithm