TCP 拥塞控制

TCP拥塞控制这一块东西比较多~这里只总结重要的。

TCP的拥塞控制主要依赖于 拥塞窗口(congestion window, cwnd) 和 慢启动阈值(slow start threshold, ssthresh)。cwnd是发送端根据网络的拥塞程度所预设的一个窗口大小,而ssthresh则是慢启动窗口的阈值,cwnd超过此阈值则转变控制策略。

TCP拥塞控制的主要算法有 慢启动(Slow Start)、拥塞避免(Congestion Avoidance)、快速重传(Fast Retransmit)、快速恢复(Fast Recovery)等。

Slow Start

如果TCP连接一建立就向服务器大量发包,很容易导致拥塞。因此,新建立的连接不能一开始就大量发送数据包,而是应该根据网络状况,逐步地增加每次发送数据包的量,这就是慢启动。慢启动通常在新建立TCP连接或由于RTO而丢包时执行。

具体来说,新建TCP连接时,cwnd需初始化为一个或几个最大发送报文段大小(send maximum segment size, SMSS)。具体规则(IW为初始窗口大小):

IW = 1*(SMSS) (if SMSS <= 2190 bytes)

IW = 2*(SMSS) and not more than 2 segments (if SMSS > 2190 bytes)

IW = 3*(SMSS) and not more than 3 segments (if 2190 ≥ SMSS > 1095 bytes)

IW = 4*(SMSS) and not more than 4 segments (otherwise)

发送端按照cwnd大小发送数据,每当数据被确认时,cwnd就以2为倍数进行指数级增长,即 $cwnd{n}=2*cwnd{n-1}$

比如:

1
2
3
4
5
6
7
开始 ---> cwnd = 1*SMSS
经过一个RTT ---> cwnd = 2*SMSS
经过两个RTT ---> cwnd = 2^2=4*SMSS
经过三个RTT ---> cwnd = 2^3=8*SMSS

如果带宽为W,那么经过 $RTT log_{2}W$ 时间就可以占满带宽。

显然,慢启动并不慢,cwnd会飞速增长。但是cwnd不能无限制地进行指数增长。当cwnd值超过慢启动阈值(ssthresh)时,慢启动过程结束,进入拥塞避免阶段。拥塞避免算法将在下面总结。

Congestion Avoidance

当cwnd值超过ssthresh值时,慢启动过程结束,进入拥塞避免阶段。在拥塞避免阶段,cwnd将不再呈指数增长,而是呈线性增长。一般来说:

  • 收到一个ACK时,cwnd = cwnd + 1/cwnd
  • 当每过一个RTT时,cwnd = cwnd + 1

这样放缓了拥塞窗口的增长速率,避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。

拥塞状态

在慢启动阶段与拥塞避免阶段,只要判断发送方出现丢包,就会进行相应的控制。有两种情况:

(1) 等待RTO超时,重传数据包,此时TCP反应强烈:

  1. 将ssthresh降低为此时cwnd的一半
  2. 将cwnd重新设为初始值(IW)
  3. 重新进入慢启动阶段

原则:加法增大、乘法减小。

(2) 连续收到3个duplicate ACK时,重传数据包,无须等待RTO。此情况即为下面的快速重传。

Fast Retransmit

TCP在收到一个乱序的报文段时,会立即发送一个重复的ACK,并且此ACK不可被延迟。

如果连续收到3个或3个以上重复的ACK,TCP会判定此报文段丢失,需要重新传递,而无需等待RTO。这就叫做快速重传。

注:快速重传始于BSD 4.3 Tahoe,但Tahoe的TCP实现没有包含快速恢复阶段,快速重传后会退回至慢启动阶段。

Fast Recovery

快速恢复是指快速重传后直接进入拥塞避免阶段而非慢启动阶段。总结一下快速恢复的步骤(以SMSS为单位):

  1. ​当收到3个重复的ACK时,将ssthresh设置为cwnd的一半(ssthresh = cwnd/2),然后将cwnd的值设为ssthresh加3(cwnd = ssthresh + 3),然后快速重传丢失的报文段
  2. 每次收到重复的ACK时,cwnd增加1(cwnd += 1),并发送1个packet(如果允许的话)
  3. 当收到新的ACK时,将cwnd设置为第一步中ssthresh的值(cwnd = ssthresh),代表恢复过程结束

快速恢复后将进入拥塞避免阶段。

注:快速恢复始于BSD 4.3 Reno。

NewReno

快速恢复存在的一个问题是它只能针对一个packet重传而不能针对多个packet,这样会RTO到吐。因此后来标准提出了NewReno作为Reno的补充。NewReno可以解决一个窗口内多个packet丢失的情况(Partial ACK),即NewReno算法需要处理完该窗口内所有packet的ACK后方可结束恢复状态。这是一种激进的优化,适合不支持SACK的情况。(通过recovery变量实现)

SACK

SACK维持一个名为pipe的变量,代表流量的估计值。发包的时间由pipe决定。具体细节可见 RFC 2018

SACK可能会出现pipe计算不精确的情况,这样会使算法退化回Tahoe。Forward Acknowledgment (FACK)是对SACK的改进,它可以精确地计算pipe值。

其它

更多的拥塞控制算法见TCP Congestion Avoidance Algorithm

文章目录
  1. 1. Slow Start
  2. 2. Congestion Avoidance
  3. 3. 拥塞状态
  4. 4. Fast Retransmit
  5. 5. Fast Recovery
  6. 6. NewReno
  7. 7. SACK
  8. 8. 其它