linux网络编程系列(十二)--滑动窗口、拥塞控制、断线重连机制

1,267 阅读7分钟

1. 滑动窗口

我们都知道TCP是可靠的协议,而可靠性很多时候就是来自于TCP的确认重传机制,在确认重传的基础上,就实现了滑动窗口协议,滑动窗口主要有两个作用:

  • 保证TCP的可靠性;
  • 提供TCP的流控特性;

1.1 滑动窗口保证可靠性

所谓窗口其实就是表示一段存放在发送缓冲区中的、可以被发送者发送的字节序列,它连续的一个范围,我们就称为窗口。而滑动则是随着数据不断的被发送,那么窗口就会在缓冲区中向前移动。 比如:

  • TCP建立连接后,B告诉A自己的接收窗口大小,比如是20;
  • 然后A发送了11个字节,发送窗口位置不变,B接收到了乱序数据;
  • 只有当A成功发送了数据,并且接收到了B返回来的确认之后,才会按顺序向前移动发送窗口,这样就避免了超时后数据的重复发送; 注意:若是B未在规定时间返回确认,那么A端会进行重复发送

1.2 滑动窗口进行流控

流量控制方面主要有两个要点需要掌握。一是TCP利用滑动窗口实现流量控制的机制;二是如何考虑流量控制中的传输效率。

1.2.1 流量控制

所谓流量控制,主要是接收方传递信息给发送方,使其不要发送数据太快,是一种端到端的控制。主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。

这里面涉及到一种情况,如果B已经告诉A自己的缓冲区已满,于是A停止发送数据;等待一段时间后,B的缓冲区出现了富余,于是给A发送报文告诉A我的rwnd大小为400,但是这个报文不幸丢失了,于是就出现A等待B的通知||B等待A发送数据的死锁状态。为了处理这种问题,TCP引入了持续计时器(Persistence timer),当A收到对方的零窗口通知时,就启用该计时器,时间到则发送一个1字节的探测报文,对方会在此时回应自身的接收窗口大小,如果结果仍未0,则重设持续计时器,继续等待。

1.2.2 传输效率

一个显而易见的问题是:单个发送字节单个确认,和窗口有一个空余即通知发送方发送一个字节,无疑增加了网络中的许多不必要的报文,所以我们的原则是尽可能一次多发送几个字节,或者窗口空余较多的时候通知发送方一次发送多个字节。对于前者我们广泛使用Nagle算法,即:

  • 若发送应用进程要把发送的数据逐个字节地送到TCP的发送缓存,则发送方就把第一个数据字节先发送出去,把后面的字节先缓存起来;
  • 当发送方收到第一个字节的确认后(也得到了网络情况和对方的接收窗口大小),再把缓冲区的剩余字节组成合适大小的报文发送出去;
  • 当到达的数据已达到发送窗口大小的一半或以达到报文段的最大长度时,就立即发送一个报文段; 对于后者我们往往的做法是让接收方等待一段时间,或者接收方获得足够的空间容纳一个报文段或者等到接收缓存有一半空闲的时候,再通知发送方发送数据。

2. 拥塞控制

计算机网络中的带宽、交换结点中的缓存、路由器等等都是网络的资源,他们所能提供的可用资源都是有限的,如果某一时间,对网络中某一资源的需求超过了它的可用部分,网络的性能就会变坏,就像堵车一样,车的数量过多超过了路段的负荷,就是出现拥堵,类比到网络,就是拥塞控制。 注意:拥塞控制是一个全局性的过程,而滑动窗口中的流量控制则是点到点通信量的控制,他们是有本质区别的。

TCP的拥塞控制由四个核心算法组成:慢开始、拥塞避免、快重传、快恢复。

2.1 慢开始和拥塞避免

发送方维持一个叫做拥塞窗口的状态变化,拥塞窗口的大小取决于网络的拥塞程度,并且动态的在变化,而发送方的发送窗口可能等于拥塞窗口,也可能因为接收方的接收缓存不够,那么发送窗口就会小于拥塞窗口。

慢开始算法的思路是说,一开始先不发送大量的数据,需要先探测一下网络的拥塞程度,由小变大的逐渐增加拥塞窗口的大小。

拥塞避免,则是在拥塞控制中还有个慢开始门限ssthresh状态变量,假设拥塞窗口大小为cwind,那么有如下几种情况:

  • 当cwind < ssthresh时,即执行慢开始算法,当使用当前拥塞窗口发送数据,收到多次确认以后,将cwind加倍,继续发送;
  • 当cwind > ssthresh时,执行拥塞避免算法,将cwind+1,然后继续发送数据;
  • 当网络发送拥塞时,把ssthresh更新为拥塞前ssthresh值的一半,cwind重新设置为1,按照以上两种情况继续执行;

2.2 快重传和快恢复

快重传算法,其实就是要求发送方只要一连收到3个重复确认就立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到。 快重传配合使用的就是快恢复,有以下两种情况:

  • 当发送方连续收到三个重复确认后,就执行乘法减小算法,把ssthresh减半,但并不执行慢开始算法;
  • 另一种情况是,考虑到如果网络出现拥塞的话,发送方可能收不到三个连续的重复确认,就会认为网络没有出现拥塞,所以此时也不执行慢开始算法,而是将cwind大小设置为ssthresh,执行拥塞避免算法;

从全局来讲,TCP拥塞控制其实较好的保证了数据流之间的公平性,因为一旦出现丢包,就立即减半退避,可以给其他新建的数据流留有足够的空间,从而保证整个的公平性。

3. 断线重连

顾名思义,就是网络断了以后要进行重连,在网络编程中,断线重连机制是必须要有的,那么怎么设计一个断线重连机制呢?

3.1 程序设置固定重连时间

有两种情况:

  • 一是发现断线后立马重连一次,然后间隔2秒后重连,然后是4秒、6秒、8秒等;
  • 二是2秒,4秒,6秒,8秒这样去重连;

3.2 让客户设置

就是在断线后,在界面上弹出窗口让客户自己设置重连间隔,这一点在许多桌面客户端和移动app上都有体现;

3.3 监控网络状态

我们可以获得网络情况,如果是网络断开了,那么我们肯定不会去重连,但如果网络一旦好了,我们就要立马重连;