参考帖子:https://blog.csdn.net/djl806943371/article/details/88838918
流控制的原因:
发送者发送速度超过接受者处理速度是没有意义的,流控制就是为了避免发送超出接受者接受能力的数据。实现的方式是接受者给发送者反馈。两种基本方式是stop and wait和sliding windows
流控制方式一:Stop and Wait
- 一次发送一个数据包
- 发送者发送一个数据包,接收者回复一个ACK包
- 发送者收到ACK包后再发送下一个数据包
- 如果发送者超时没有收到ACK包,则重新发送数据包
最后一条可能导致出现如下错误:sender发送数据包,receiver发送ACK包,如果ACK延迟到达,超过了timeout,到达之前sender已经重发数据包,那么sender以为这个ACK是对重发数据包的确认,进而发送下一条数据包,receiver收到重发的数据包之后,再次返回ACK包,如果第二个数据包此时丢失了,sender依然收到了ACK包以为receiver收到了,那么这条数据就丢失了。
为了解决这个问题,添加了1bit counter,用于告诉receiver这条数据是新数据包还是重发的数据包,但是还是可能存在问题的:如图,如果一个条数据延迟了多个timeout,那么还是会出现问题。
因此使用stop and wait+1 bit counter要基于以下条件:
- 网络本身不会产生重复的包
- 包不会有多个timeout的延迟
流控制方式二:sliding windows
Sliding Window Sender:
-
每个segment有一个序列号;
-
持有三个变量:
Send window size(SWS);
Last acknowledgment received(LAR);
Last segment sent(LSS)。
- 满足不等式:LSS-LAR≤SWS
- 每当收到新的ACK,将LAR前推;
- 缓冲区也缓冲滑动窗口大小的数据包。
相当于有一个receive window每当收到窗口内最前面一个数据包的ACK,就将窗口往前移,这也是滑动窗口命名的缘由。
Sliding Window Receiver:
- 持有三个变量:
Receive window size(RWS);
Last acceptable segment(LAS);
Last segment received(LSR);
- 满足不等式:LAS-LSR≤RWS;
- 如果收到的数据序列号小于LAS,就发送ACK:
如果收到了1,2,3,5,缺失了4,则收到5及后面的数据时依然返回收到3对应的ACK,但是只要接受缓冲区不满5及后续内容会被存储在接收缓冲区。因为一直接收不到4对应的ACK,sender超时会重发数据包4,此时receiver收到4后会直接发送5(已经存放在缓冲区的最后一个连续的内容)对应的ACK。
TCP中发送窗口与接收窗口关系:
发送窗口示意图:发送窗口为下图中蓝色及绿色部分两部分的并集,在这之前的紫色部分为接收端已经确认接收了的包,后面的黄色部分则是不允许发送的包。发送窗口的大小是随着接收端接收窗口的大小动态变化的(稍后进行解释)。
接收窗口示意(算了,没找到合适的图哈哈):接收窗口是指接收端还可以接收的部分即缓存区空置的位置。在这个窗口之前还有其他几个部分,最前面的是已经接收并返回了ACK但是还没有被应用取走的数据,接下来是已经接收但是还没返回ACK的数据,接下来是接收窗口即空置部分。这三部分加起来是一个固定大小的缓冲区,当应用取走数据时,接收窗口将增大。
这两部分是如何互动的呢:由于TCP连接是双工的,并且建立连接之后两者地位完全对等,所以我们仅以A为发送端,B为接收端为例。当TCP三次握手的时候,B发送给A的包的首部的窗口区会标明自己接受窗口的大小,A将自己的发送窗口设置为同样大小。则A可以发送窗口内的数据。当B接收到数据后会返回ACK,并且窗口区会标明现在自己接受窗口的大小(因为发送这个数据包的之前有可能应用已经取走了部分数据,所以接收窗口的大小将介于初始大小与初始大小跟接受到的数据长度之差之间。)当A收到之后,窗口左边界移动到被ACK的数据之后一位,右边界则根据窗口大小变化。比如如果接收端接收到数据,所有数据都没被应用取走,则发送窗口右边界不会移动。