[TOC]

TCP流量控制滑动窗口协议详解

前言

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

滑动窗口

滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;
同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。
发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。
不同的滑动窗口协议窗口大小一般不同。
发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。

作用

TCP的滑动窗口主要有两个作用;

  1. 一是提供TCP的可靠性
  2. 二是提供TCP的流控特性

TCP的Window是一个16bit位字段,它代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16-1=65535个字节。

另外在TCP的选项字段中还包含了一个TCP窗口扩大因子,option-kind为3,option-length为3个字节,option-data取值范围0-14。窗口扩大因子用来扩大TCP窗口,可把原来16bit的窗口,扩大为31bit。

滑动窗口基本原理

发送窗口

对于TCP会话的发送方,任何时候在其发送缓存内的数据都可以分为4类,

  • “已经发送并得到对端ACK的”,
  • “已经发送但还未收到对端ACK的”
  • “未发送但对端允许发送的”
  • “未发送且对端不允许发送”

“已经发送但还未收到对端ACK的”和“未发送但对端允许发送的”这两部分数据称之为发送窗口(中间两部分)

接收窗口

对于TCP的接收方,在某一时刻在它的接收缓存内存在3种。

  • “已接收”
  • “未接收准备接收”
  • “未接收并未准备接收”

(由于ACK直接由TCP协议栈回复,默认无应用延迟,不存在“已接收未回复ACK”)
其中“未接收准备接收”称之为接收窗口
image.png


滑动原理如下图:
image.png

发送窗口与接收窗口关系

TCP是双工的协议,会话的双方都可以同时接收、发送数据。
TCP会话的双方都各自维护一个“发送窗口”和一个“接收窗口”。
其中各自的“接收窗口”大小取决于应用、系统、硬件的限制(TCP传输速率不能大于应用的数据处理速率)。
各自的“发送窗口”则要求取决于对端通告的“接收窗口”,要求相同

滑动窗口实现面向流的可靠性

最基本的传输可靠性来源于“确认重传”机制。
TCP的滑动窗口的可靠性也是建立在“确认重传”基础上的。
发送窗口只有收到对端对于本段发送窗口内字节的ACK确认,才会移动发送窗口的左边界。
接收窗口只有在前面所有的段都确认的情况下才会移动左边界。
当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认。
以此确保对端会对这些数据重传。

窗口的动作

  • 窗口合拢:当窗口从左边向右边靠近的时候,这种现象发生在数据被发送和确认的时候。
  • 窗口张开:当窗口的右边沿向右边移动的时候,这种现象发生在接受端处理了数据以后。
  • 窗口收缩:当窗口的右边沿向左边移动的时候,这种现象不常发生。

    滑动窗口的流控特性

    TCP的滑动窗口是动态的,我们可以想象成小学常见的一个数学题,一个水池,体积V,每小时进水量V1,出水量V2。当水池满了就不允许再注入了,如果有个液压系统控制水池大小,那么就可以控制水的注入速率和量。
    这样的水池就类似TCP的窗口。应用根据自身的处理能力变化,通过本端TCP接收窗口大小控制来对对对端的发送窗口流量限制。
    应用程序在需要(如内存不足)时,通过API通知TCP协议栈缩小TCP的接收窗口。然后TCP协议栈在下个段发送时包含新的窗口大小通知给对端,对端按通知的窗口来改变发送窗口,以此达到减缓发送速率的目的。

    1比特滑动窗口协议

    上面说的只是滑动窗口协议的理论,实际应用中又有不同。
    首先就是停等协议(stop-and-wait),这时接受方的窗口和发送方的窗口大小都是1,1个比特就够表示了,所以也叫1比特滑动窗口协议。
    发送方这时自然发送每次只能发送一个,并且必须等待这个数据包的ACK,才能发送下一个。
    虽然在效率上比较低,带宽利用率明显较低,不过在网络环境较差,或是带宽本身很低的情况下,还是适用的。看下面的流程图:
    image.png

    后退n协议

    停等协议虽然实现简单,也能较好的适用恶劣的网络环境,但是显然效率太低。
    所以有了后退n协议,这也是滑动窗口协议真正的用处,这里发送的窗口大小为n,接受方的窗口仍然为1。具体看下面的图,这里假设n=9:
    首先发送方一口气发送10个数据帧,前面两个帧正确返回了,数据帧2出现了错误,这时发送方被迫重新发送2-8这7个帧,接受方也必须丢弃之前接受的3-8这几个帧。
    后退n协议的好处无疑是提高了效率,但是一旦网络情况糟糕,则会导致大量数据重发,反而不如上面的停等协议,实际上这是很常见的,具体可以参考TCP拥塞控制。
    image.png

    选择重传协议

    后退n协议的另外一个问题是,当有错误帧出现后,总是要重发该帧之后的所有帧,毫无疑问在网络不是很好的情况下会进一步恶化网络状况,重传协议便是用来解决这个问题
    原理也很简单,接收端总会缓存所有收到的帧,当某个帧出现错误时,只会要求重传这一个帧,只有当某个序号后的所有帧都正确收到后,才会一起提交给高层应用。
    重传协议的缺点在于接受端需要更多的缓存。