TCP的流量控制

时间:2023-01-27 05:20:45

TCP协议作为一个可靠的面向字节流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现。

要区分TCP的流量控制和拥塞控制:

流量控制是发送方的发送数据的速度不能太快,要考虑到接收方的接收缓冲区的大小,不然数据发送的太快,就可能导致接收方的接收缓冲区数据溢出。所以在流量控制中发送方的发送窗口大小受接收方的接收缓冲区的大小的制约。

拥塞控制是发送方根据当前网络的拥塞程度调整自己的发送窗口的大小。当网络出现拥塞的时候,减少发送数据的数量,当网络空闲的时候,适时的增加数据的发送量。

1. 以字节为单位的滑动窗口

TCP协议的两端分别为发送者A和接收者B,由于是全双工协议,因此A和B应该分别维护着一个独立的发送缓冲区和接收缓冲区,由于对等性(A发B收和B发A收),我们以A发送B接收的情况作为例子。

现在假设A收到了B发来的确认报文段,其中窗口数是20(字节),确认号是31(折表明B期望收到的下一个序号是31,而序号30为止的数据都已经收到)。根据这两个数据A就构造出了自己的发送窗口。

TCP的流量控制

看看A的发送窗口,发送窗口表示:在没有收到B确认的情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时的时候重传。

发送窗口后沿的后面部分表示已发送且已收到确认的字节,这些数据显然不在需要保留了,而发送数据前沿的前面部分表示不允许发送的,因为接收方的接收缓冲区不够大,不能暂存这么多的数据。

发送窗口的位置由窗口前沿和窗口后沿的位置共同决定

发送窗口后沿的变化情况有两种:不动(没有收到新的确认)、前移(收到了新的确认)。发送窗口的后沿不可能向后移动,因为不能撤销掉已经收到的数据。

发送窗口的前沿也有两种变换情况:不动(没有收到新的确认,对方通知的窗口数大小也没有变化或者收到了新的确认,但是对方通知的窗口数减少了,正好使得发送窗口的前沿不动)、前移(这个很正常)。

注意:如果对方通知的窗口缩小了,那么发送窗口的前沿就会向后收缩,这时TCP的标准强烈不赞成的。


如下图所示:A发送了序号为31~41的数据,现在的状态就是下面这个样子:

TCP的流量控制

小于P1的是已发送并收到确认的部分,大于P3的是不允许发送的部分。

P3-P1=A的发送窗口

P2-P1=已发送但尚未收到确认的字节数

P3-P2=允许发送当尚未发送的字节数(可用窗口或者有效窗口)

再来看一下这时的接收窗口的状况:

TCP的流量控制

接收窗口内的31~50的序号是允许接收的,如上图所示的这个样子,B接收到了32、33的两个序号,但是31号还没有到,收到的数据不是按序到达的,这时B发送的确认报文确认号仍然要是31(期望收到的序号),而不是32或者33。

当B收到了31号的数据,就把31~33的数据交付,并删除这些数据,接着把接收窗口向前移动3个序号,同时给A发送确认,其中的窗口数仍为20,但是确认号变为34,表明B收到了序号33为止的数据。A收到了B的确认后,就可以把发送窗口向前滑动3个序号,但是指针P2不动,移动的只是指针P1和P3.

TCP的流量控制

注意到了,B收到了序号为37、38、40的数据,但是他们没有按序达到,所以只能暂时存放在接收窗口中。


A继续发送完42~53的数据后,指针P2不断的向前移动,最后和指针P3重合,A的有效窗口缩小为0,因此必须停止发送。

TCP的流量控制

这时存在这种情况,A不能再继续发送,等待B的确认。这些数据也都正确的到达B,同时B给出了确认,但是这些确认都滞留在网络中,所以A要设置一个超时计时器,在经过一段时间后,如果A还没有收到B有效的确认,就要重传整个发送窗口中的数据。知道收到B的确认为止。

窗口滑动总结:

整个流量控制就是限制发送方的数据的发送速度,让接收方能够及时的处理,不至于数据从接受缓冲区中溢出。所以这里涉及两个概念:

发送方的应用进程把字节流写入TCP的发送缓冲,接收方的应用进程从TCP的接收缓冲中读取字节流。

缓冲区的空间和序号的空间都是有限的,并且都是循环使用的,所以可以把他们看成是圆环形状的。


2. 利用滑动窗口实现流量控制

如下图所示:

TCP的流量控制

在A和B建立连接的时候,也就是在TCP的三次握手的时候,发送端和接收端进行了窗口大小的协商,发送方的发送窗口不能超过接收方给出的接收窗口的数值。

最后窗口的大小减少到了0,也就是不允许发送方再发送数据,这种使发送方暂停发送的状态将持续到主机B重新发送一个新的窗口值为止。但是可能发生这样的情况,B向A发送了窗口值为400的报文,但是这个报文在传送的过程中丢失了,那么A就一直等待B发送的非零窗口的通知,而B也一直等待A发送的数据,这样就发生了死锁。

为了解决这个问题,TCP为每一个连接设置一个持续计时器,只要TCP连接的一方收到了零窗口的通知,那么就启动这个定时器。如果到了定时器的时间,就发送一个零窗口的探测性报文。而对方就在确认这个探测性报文段时给出了现在的窗口值。如果窗口值还是为零,就继续启动定时器。