万字详文:TCP 拥塞控制详解( 六 )


五、拥塞控制拥塞的发生是因为路由器缓存溢出,拥塞会导致丢包,但丢包不一定触发拥塞 。拥塞控制是快速传输的基础 。一个拥塞控制算法一般包括慢启动算法、拥塞避免算法、快速重传算法、快速恢复算法四部分 。

万字详文:TCP 拥塞控制详解

文章插图
拥塞窗口示意图
5.1 慢启动算法不同拥塞算法慢启动的逻辑有所不同,经典的 NewReno 慢启动的算法如下:
  1. 连接建好的开始先初始化 cwnd = 10,表明可以传 10 个 MSS 大小的数据 。
  2. 每当收到一个 ACK,cwnd 加 1 。这样每当过了一个 RTT,cwnd 翻倍,呈指数上升 。
  3. 还有一个 ssthresh(slow start threshold),是一个上限 。当 cwnd >=ssthresh 时,就会进入“拥塞避免算法” 。
Linux 3.0 后采用了 google 的论文《An Argument for Increasing TCP’s Initial Congestion Window》的建议——把 cwnd 初始化成了 10 个 MSS 。而 Linux 3.0 以前,比如 2.6,Linux 采用了[RFC3390] 的建议,cwnd 跟 MSS 的值来变,如果 MSS < 1095,则 cwnd = 4;如果 MSS >2190,则 cwnd = 2;其它情况下,则是 3 。
5.2 拥塞避免算法当 cwnd 增长到 sshthresh 时,就会进入“拥塞避免算法” 。拥塞避免算法下 cwnd 成线性增长,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍 。这样就可以避免拥塞窗口快速增长的问题 。
每收到一个 ack 时 cwnd 的变化:cwnd = cwnd + 1 / cwnd5.3 快速重传算法快速重传算法主要用于丢包检测,以便能更快重传数据包,更早的调整拥塞状态机状态,从而达到持续升窗的目的 。具体重传策略见第三节 重传机制 。
5.4 快速恢复算法当检测到丢包时,TCP 会触发快速重传并进入降窗状态 。该状态下 cwnd 会通过快速恢复算法降至一个合理值 。从历史发展来看,分为四个个阶段 。
5.4.1 BSD 初始版本
  1. 收到 3 次重复 ACK,ssthresh 设为 cwnd/2,cwnd = cwnd / 2 + 3;
  2. 每收到一个重复 ACK,窗口值加 1;
  3. 收到非重复 ACK,窗口设为 ssthresh,退出
优点:在快速恢复期间,可以尽可能多的发送数据缺点:由于快速恢复未完成,尽可能多发送可能会加重拥塞 。#### 5.4.2 [RFC3517]版本 1) 收到 3 次重复 ACK,ssthresh 设为 cwnd/2,cwnd = cwnd / 2 + 3; 2)每收到一个重复 ACK,窗口值加 1/cwnd; 3) 收到非重复 ACK,窗口设为 ssthresh,退出 。
优点:在快速恢复期间,可以尽少量的发送数据(有利于拥塞恢复),且在快速恢复时间段的最后阶段,突发有利于抢带宽 。
缺点:快速恢复末期的突发不利于公平性 。
5.4.2 Linux rate halving 算法Linux 上并没有按照 [RFC3517] 标准实现,而是做了一些修改并运用到内核中 。
1.收到 3 次重复 ACK,sshthresh 设置为 cwnd/2,窗口维持不变 。2.每收到两个 ACK(不管是否重复),窗口值减 1:cwnd = cwnd - 1 。3.新窗口值取 cwnd = MIN(cwnd, in_flight+1) 。4.直到退出快速恢复状态,cwnd = MIN(cwnd, ssthresh) 。
优点:在快速恢复期间,取消窗口陡降过程,可以更平滑的发送数据 缺点:降窗策略没有考虑 PIPE 的容量特征,考虑一下两点:
a.如果快速恢复没有完成,窗口将持续下降下去 b.如果一次性 ACK/SACK 了大量数据,in_flight 会陡然减少,窗口还是会陡降,这不符合算法预期 。
5.4.3 prr 算法 [RFC6937]PRR 算法是最新 Linux 默认推荐的快速恢复算法 。prr 算法是一种按比例降窗的算法,其最终效果是:
1.在快速恢复过程中,拥塞窗口非常平滑地向 ssthresh 收敛;2.在快速恢复结束后,拥塞窗口处在 ssthresh 附近
PRR 降窗算法实时监控以下的变量:in_flight:它是窗口的一个度量,in_flight 的值任何时候都不能大于拥塞窗口的大小 。
prr_delivered:本次收到 ACK 进入降窗函数的时候,一共被 ACK 或者 SACK 的数据段数量 。它度量了本次从网络中清空了哪些数据段,从而影响 in_flight 。
prr_out:进入快速恢复状态后已经被发送了多少数据包 。在 transmit 例程和 retransmit 例程中递增 。
to_be_out:当前还可以再发多少数据包 。
算法原理根据数据包守恒原则,能够发送的数据包总量是本次接收到的 ACK 中确认的数据包的总量,然而处在拥塞状态要执行的并不是这个守恒发送的过程,而是降窗的过程,因此需要在被 ACK 的数据包数量和可以发送的数据包数量之间打一个折扣,PRR 希望达到的目标是:
万字详文:TCP 拥塞控制详解


推荐阅读