tcp 拥塞控制(TCP协议详解-拥塞控制)
tcp 拥塞控制(TCP协议详解-拥塞控制)如果太小,会引起明显的网络延迟;如果太大,则容易导致网络拥塞。发送端需要合理地选择SWND的大小:它们或者部分或者全部实现了上述4个部分。通过查看上面截图中的参数,可以找到当前机器所使用的拥塞控制算法。拥塞控制的最终受控变量是发送端向网络一次写入(收到其中第一个数据的确认之前)的写入量,我们称之为SWND(Send Window,发送窗口)。不过,发送端最终以TCP报文段来发送数据,所以SWND限定了发送端能连续发送的TCP报文段数量。这些TCP报文段的最大长度(仅指数据部分)称为SMSS(Sender Maximum Segment Size,发送者最大段大小),其值一般等于MSS。
TCP模块还有一个重要的任务就是:
- 提高网络利用率
- 降低丢包率
- 并保证网络资源对每条数据流的公平性
这就是所谓的拥塞控制。
TCP拥塞控制的标准文档就是RFC 5681,其中详细介绍了拥塞控制的四个部分:
- 慢启动 slow start
- 拥塞避免 congestion avoidance
- 快速重传 fast retransmit
- 快速恢复fast recovery
拥塞控制算法在Linux下有多种实现,比如reno算法、vegas算法和cubic算法等。
它们或者部分或者全部实现了上述4个部分。
通过查看上面截图中的参数,可以找到当前机器所使用的拥塞控制算法。
拥塞控制的最终受控变量是发送端向网络一次写入(收到其中第一个数据的确认之前)的写入量,我们称之为SWND(Send Window,发送窗口)。不过,发送端最终以TCP报文段来发送数据,所以SWND限定了发送端能连续发送的TCP报文段数量。这些TCP报文段的最大长度(仅指数据部分)称为SMSS(Sender Maximum Segment Size,发送者最大段大小),其值一般等于MSS。
发送端需要合理地选择SWND的大小:
如果太小,会引起明显的网络延迟;如果太大,则容易导致网络拥塞。
接收方可通过其接收通过窗口(RWND)来控制发送端的SWND。但这显然不够,所以发送端引入了一个称为拥塞窗口(Congestion Window CWND)的状态变量。实际上的SWND值就是RWND和CWND中的较小者。
拥塞控制的输入和输出
RTT——Round Trip Time,也就是一个数据包从发出去到回来的时间
慢启动和拥塞避免TCP连接建立好之后,CWND将被设置成初始值IW(Initial Window),其大小为2~4个SMSS。但新的Liunx内核提高了该初始值,以减小传输滞后。此时发送端最多能发送IW个字节的数据。此后发送端每收到接收端的一个确认,则:
CWND = min(N SMSS)
其中N是此次确认中包含的之前未被确认的字节数。
这样一来,CWND将按照指数形式扩大,这就是所谓的慢启动。慢启动算法的理由是,TCP模块刚开始发送数据时并不知道网络的实际状况,需要用一种试探的方式平滑地增加CWND的大小。
但是如果不施加其他手段,慢启动必然使得CWND很快膨胀并最终导致网络瘫痪。因此TCP拥塞控制中定义了另一个重要的状态变量:慢启动门限(slow start threshold size ssthresh)。当CWND的大小超过该值时,TCP拥塞控制将进入拥塞避免阶段。
拥塞避免算法使得CWND按照线性方式增加,从而减缓其扩大。RFC 5681中提到了如下两种实现方式:
- 每个RTT时间内按照之前的公式计算新的CWND,而不论该RTT时间内发送端收到多少个确认。
- 每收到一个对新数据的确认报文段,就按照下面的公式来更新CWND
CWND = SMSS × SMSS/CWND
下图粗略描述了慢启动和拥塞避免发生的时机和区别。该图中,我们以SMSS为单位来显示CWND(实际上它是以字节为单位的),以次数为单位来显示RTT,这只是为了方便讨论。另外,我们假设当前的ssthresh是16SMSS大小(当然,实际上显然远不止这么大)。
以上我们介绍了发送端在未检测到拥塞时所采用的积极避免拥塞的方法。
下面是拥塞发生时(可能发生在慢启动阶段或者拥塞避免阶段)拥塞控制的行为。
不过我们先要弄清楚发送段怎么判断拥塞已经发生。其判断依据有两个:
- 传输超时,或者TCP重传定时器溢出。
- 接收到重复的确认报文段。
拥塞控制对这两种情况有不同的处理方式。
对第一种情况仍然使用慢启动和拥塞避免。
对第二种情况则使用快速重传和快速恢复(如果真发生拥塞的话)。
这里需要注意的是,如果第二种情况发生在重传定时器溢出之后,则也被拥塞控制当成第一种情况来对待。
如果发送端检测到拥塞发生是由传输超时,即上述第一种情况,那么它将执行重传并做如下调整:
ssthresh = max(FlightSize/2,2×SMSS)
CWMD <= SMSS
其中FlightSize是已经发送但未收到确认的字节数。这样调整之后,CWMD将小于SMSS,那么也必然小于新的慢启动门限制ssthresh,故而拥塞控制再次进入慢启动阶段。
快速重传和快速恢复很多情况下,发送端都可能接收到重复的确认报文段,比如TCP报文段丢失,或者接受端收到乱序TCP报文段并重排之等。拥塞控制算法需要判断当收到重复的确认报文段时,网络是否真的发生了拥塞,或者说TCP报文段是否真的丢失了。具体做法是:
发送端如果连续收到3个重复的确认报文段,就认为是拥塞发生了。然后它启用快速重传和快速恢复算法来处理拥塞:
- 当收到第三个重复的确认报文段时,按照之前的公式重新计算ssthresh,然后立即重传丢失的报文段,并设置
CWND = ssthresh 3 * SMSS
- 每次收到一个重复的确认时,设置CWND = CWND SMSS。此时发送的可以发送新的TCP报文段(如果新的CWND允许的话)。
- 当收到新数据确认时,设置CWND = ssthresh(ssthresh是新的慢启动门限值,由第一步计算得到)。
快速重传和快速恢复完成之后,拥塞控制将恢复到拥塞避免阶段。