Bootstrap

《linux高性能服务器编程》学习笔记之拥塞控制

拥塞控制概述

拥塞控制是TCP协议的一个重要的任务,拥塞控制的目的是提高网络利用率,降低丢包率,并保证网络资源对每条数据流的公平性。

拥塞控制的四个部分:慢启动、拥塞避免、快重传和快速恢复。拥塞控制算法在Linux下有多种实现,比如reeno算法、vegas算法和cubic算法等。它们或部分或全部实现了上述四个部分。你可以在/proc/sys/ipv4/tcp_congestion_control中查看目前使用的拥塞控制算法。

拥塞控制最根本就是控制发送端向网络一次连续读写的数据量,我们称为SWND(Send Window,发送窗口)。我们本质上通过TCP报文段来发送数据,而SWND就限制了发送TCP报文段的数量。SWND的单位是字节。

合理的设置SWND的大小。如果SWND太小,会引起明显的网络延迟;反之,如果SWND太大,则容易导致网络拥塞。接收方可通过其接收通告窗口来控制发送端的SWND,但这显然不够,所以发送端引入了一个称为拥塞窗口CWND(Congestion Window)的状态变量。实际的SWND值是RWND和CWND中的较小值。
在这里插入图片描述
因此,我们接下来的讨论将围绕如何控制拥塞窗口的大小,因为接收窗口是由接收端决定的。

拥塞控制的四部分都是围绕CWND进行的。

拥塞控制和拥塞避免

拥塞控制和拥塞避免如图
在这里插入图片描述

慢启动和拥塞避免

TCP连接建立好之后,CWND将被设置成初始值IW(Initial Window),其大小根据不同的拥塞控制算法不同,一般为2~4个SMSS。但新的Linux内核提高了该初始值,**以减少传输滞后。**此时发送端最多能发送IW字节的数据。此后发送端收到接收端的一个确认,其CWND就按照CWND+=min(N,SMSS)增加。

其中N是此次确认中包含的走之前发送但未被确认的字节数。这样一来,CWND就按照指数型增大。

这里解释一下,可能有人看不懂了,CWND+=min(N,SMSS)根据这个式子CWND明明是线性增长的,怎么就指数增长了?前面提到SWND限制了发送的报文段数量,而CWND又一定程度上决定了SWND,当CWND增加那么TCP报文段的数量也会增加,而CWND的增长是在每收到一次TCP确定报文段后。举个例子,现在CWND的大小能发送4个TCP报文段,这四个TCP报文段都会受到确认,那么这个RTT内,就会增长4次。在下一个RTT内就会增长8次。(后面会解释RTT)

这就是所谓的慢启动,慢启动指的只是开始时传输的数据较小,但是它的增速是巨大的。慢启动算法的理由是,TCP模块刚开始发送数据时并不知道网络的实际情况,需要用一种试探的方式平滑地增加CWND的大小。

但是如果不使用其他手段,慢启动必然会导致CWND很快膨胀,并最终导致网络拥塞。因此TCP拥塞控制中定义了另一个重要的状态变量:慢启动门限大小(Slow Start Threshold Size),当CWND的值超过该值时,TCP拥塞控制将进入拥塞避免阶段。

拥塞避免算法是的CWND按照线性增长方式增加,从而减缓了其扩大。RFC 5681中提到两种实现方式

  • 每个RTT时间内按照CWND+=(N,SMSS)计算新的CWND,而不论该RTT时间内发送端收到多少个确认。
  • 每收到一个对新数据的确认报文段,就按照CWND+=SMSS*SMSS/CWND

在这里插入图片描述

RTT(Round-Trip Time,往返旅行时间):发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时间。在CWND大小没有达到ssthresh之前,每个RTT时间内会发送多个TCP报文段,每个报文段收到确认回复时都会增加CWND,在达到ssthresh之后,一个RTT时间内只会增长一次CWND的值。

慢启动和拥塞避免算法都是避免网络拥塞的方法,下面介绍当发生网络拥塞时采取的措施。

首先要明确发送端如何确认网络拥塞已经发生:

  • 传输超时,TCP重传定时器超时。
  • 接收到重复的确认报文。

拥塞控制对这两种情况有不同的处理方式。第一种情况采用慢启动和拥塞避免。对第二种情况使用快速重传和快速恢复。注意,第二种情况如果发生在重传定时器超时后,则也被拥塞控制当成第一种情况对待。

针对第一种情况,将会执行以下调整:将

ssthresh = max(FlightSize,2*SMSS);

其中FlightSize是已经发送但未收到确认的字节数。CWND调整到IW,此时再次进入慢启动阶段。

快速重传和快速恢复

这两种算法就应对与第二种情况,有时候发送端收到重复的确认报文段,可能因为接收端接受的数据中间一部分报文段的丢失,或者接收端收到乱序TCP报文段并重排之等。

当接收端收到报文段的顺序不符合序号,即在两个报文段之间的报文段没收到,就会立即发送三条重复确认报文,接收方收到三条重复的确认报文段后,就立即重传确认报文段后面的报文,通过尽早重新传送未被确认的报文段,减少了等待超时时间和减少重传报文段数量,使得整个网络吞吐量提高了20%。

为什么要使用快速重传?快速重传使用是有条件的,如果此时发生了网络拥塞,采用快速重传岂不是更加加剧了网络拥塞?因此当发送方收到三条重复的确认报文并且还未超过重传定时器时长,认为网络很可能没有发生拥塞,因此采用了快重传。

但是为了保险起见,防止可能真的发生了网络拥塞,所以采用了快恢复算法。

快恢复的执行就是将ssthresh设置为减半的大小。CWND设置和ssthresh一样,然后执行拥塞避免算法。

补充
也有的快恢复算法再开始时会将CWND值再增大一些,即等于ssthresh+3×MSS。理由是:既然发送方收到三个重复的确认,就表明有三个分组已经离开了网络。这三个分组不再消耗网络资源而是停留在接收方的缓存中。可见现在网络中并不是堆积了分组而是减少了三个分组。因此可以适当把拥塞窗口增大些。

发送端如果连续收到3个重复的确认报文段,就认为是拥塞发生了。然后它启用快速重传和快速恢复算法来处理拥塞
过程如下:

  • 当 收到三个重复的确认报文段时,按照CWND=ssthresh+3*SMSS;
  • 每次收到一个重复的确认时,设置CWND = CWND+SMSS.此时发送端可以发送新的TCP报文段(如果新的CWND允许的话).
  • 当收到新数据的确认时,设置CWND = ssthresh(ssthresh是新的慢启动门限值).
    快速冲传和快速回复完成之后,拥塞控制将恢复到拥塞避免阶段.

总结

  1. 需要明白控制网络拥塞就得控制每次发送数据的大小。
  2. 决定发送数据的大小要从接收方的接受能力和当前网络的传输能力一起考虑。
;