【Network】TCP 的流量控制(Flow Control) - 滑动窗口(Sliding Window)

Posted by 西维蜀黍 on 2019-05-07, Last Modified on 2024-09-25

滑动窗口(Sliding Window)实现流量控制(Flow Control)

如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。所谓 ** 流量控制(Flow Control)** 就是让发送方的发送速率不要太快,要让接收方来得及接收。

利用 ** 滑动窗口(Sliding Window)** 机制可以很方便地在 TCP 连接上实现对发送方的流量控制。

设 A 向 B 发送数据。在连接建立时,B 告诉了 A:“我的接收窗口是 rwnd = 400”(这里的 rwnd 表示 receiver window) 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP 的窗口单位是字节,不是报文段。TCP 连接建立时的窗口协商过程在下图中没有显示出来。

再设每一个报文段为 100 字节长,而数据报文段序号的初始值设为 1。大写 ACK 表示首部中的确认位 ACK,小写 ack 表示确认字段的值 ack。

从图中可以看出,B 进行了三次流量控制。第一次把窗口减少到 rwnd = 300 ,第二次又减到了 rwnd = 100 ,最后减到 rwnd = 0 ,即不允许发送方主机 A 再发送数据。

这使得发送方 A 会保持暂停发送数据的状态,直到主机 B 重新发出一个新的窗口值。B 向 A 发送的三个报文段都设置了 ACK = 1 ,只有在 ACK=1 时确认号字段才有意义。

TCP为每一个连接设有一个持续计时器(persistence timer)。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口控测报文段(携1字节的数据),那么收到这个报文段的一方就重新设置持续计时器。

TCP 的窗口机制

大家都知道 TCP 和 UDP 的区别是,TCP 是基于连接的 (三次握手),而且 TCP 数据的传输是可靠的,所谓可靠是指,依托序列号及确认号机制让 TCP 的传输过程中,只要出现丢包就会重传。

但是 TCP 的确认机制也让 TCP 连接双方的数据传输速度变慢,也就是说,一方发送数据需要等待对方的确认才继续发送后续数据。这就体现的窗口机制的作用,所谓窗口,也就是充分利用双方的带宽及缓冲区 (Buffer)。举个栗子,发送方不必等待对方的确认,可以连续发送多个数据包给对方,而对方可以暂时把这些数据存放在缓冲区,并给对方一个确认。这样,可以大大增加数据传输的速度。

这样做的问题是一旦当接收方的缓冲区填满或即将填满时,就会产生不堪重负的情况,那么这就需要 TCP 窗口的实时更新机制

举个栗子,接收方窗口大小设置的是 50000,也就是说发送方可以不必等待确认,而一次性发送 50000 字节的数据,一旦接收方意识到不堪重负的情况,可以发送窗口更新通知告诉发送方,现在的窗口大小是 30000,请减少一次性发送数据的字节数。某些极端情况,接收方的 Buffer 完全填满,这时,会发送 ZeroWindowSize 通知,让发送方暂时停止数据传输,并等待下一个确认通知。

下面我们来看看抓包中是如何体现窗口的特征的:

红框中的那一行可能会让大家疑惑。为什么会有一个 Calculated window size 呢?

这张图中我们可以看到,windows size 的值是 2060,但是 Calculated Windows Size 的值却是 131840,这是怎么回事呢?

事实上,在上图这个数据报对应连接的 TCP 三次握手中的 SYN 数据报中(对应下图),在 Options 区包含一个 Window Size Scaling,设计出这个选项是因为如今的带宽已经大规模提升,千兆到桌面也是一件常事儿,因此,65535 长度的窗口大小已经显得有些小了,为了突破这个限制,便有了 Window Size Scaling 选项。

我们就具体来看看 TCP Header 中的这个 Option:TCP Window Size Scaling。

可以看到这个字段的值为 6,也就是要将 Window Size 的值左移六 2 位,即乘以 64,因此,上图中我们看到 window size 值是 2060,但是真正选用的值却是 2060 * 64 = 131840。

Reference