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}$
比如:
|
|
如果带宽为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反应强烈:
- 将ssthresh降低为此时cwnd的一半
- 将cwnd重新设为初始值(IW)
- 重新进入慢启动阶段
原则:加法增大、乘法减小。
(2) 连续收到3个duplicate ACK时,重传数据包,无须等待RTO。此情况即为下面的快速重传。
Fast Retransmit
TCP在收到一个乱序的报文段时,会立即发送一个重复的ACK,并且此ACK不可被延迟。
如果连续收到3个或3个以上重复的ACK,TCP会判定此报文段丢失,需要重新传递,而无需等待RTO。这就叫做快速重传。
注:快速重传始于BSD 4.3 Tahoe,但Tahoe的TCP实现没有包含快速恢复阶段,快速重传后会退回至慢启动阶段。
Fast Recovery
快速恢复是指快速重传后直接进入拥塞避免阶段而非慢启动阶段。总结一下快速恢复的步骤(以SMSS为单位):
- 当收到3个重复的ACK时,将ssthresh设置为cwnd的一半(ssthresh = cwnd/2),然后将cwnd的值设为ssthresh加3(cwnd = ssthresh + 3),然后快速重传丢失的报文段
- 每次收到重复的ACK时,cwnd增加1(cwnd += 1),并发送1个packet(如果允许的话)
- 当收到新的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。