tcp拥塞控制之慢启动和拥塞避免

TCP拥塞控制概览

TCP的拥塞控制算法被设计用来防止快速的发送者压垮整个网络。如果一个发送TCP发
送包的速度要快于一个中间路由器转发的速度,那么该路由器就会开始丢弃包。这将会导致
较高的包丢失率,其结果是如果TCP保持以相同的速度发送这些被丢弃的分段的话就会极大
地降低性能。TCP的拥塞控制算法在下列两个场景中是比较重要的。

. . .

  • 在连接建立之后:此时(或当传输在一个已经空闲了一段时间的连接上恢复时),发
    送者可以立即向网络中注入尽可能多的分段,只要接收者公告的窗口大小允许即可。
    (事实上,这就是早期的TCP实现的做法。)这里的问题在于如果网络无法处理这种分
    段洪泛,那么发送者会存在立即压垮整个网络的风险。
  • 当拥塞被检测到时:如果发送TCP检测到发生了拥塞,那么它就必须要降低其传输速
    率。TCP是根据分段丢失来检测是否发牛了拥塞,因为传输错误率是非常低的,即如
    果一个包丢失了,那么就认为发生了拥塞。

TCP的拥塞控制策略组合采用了两种算法:

  • 慢启动
  • 拥塞避免。

慢启动算法会使发送TCP在开始的时候以低速传输分段,但同时允许它以指数级的速
度提高其速率,只要这些分段都得到接收TCP的确认。慢启动能够防血一个快速的TCP发送
者压垮整个网络。但如果不加限制的话,慢启动在传输速率上的指数级增长意味着发送者在
短时间内就会压垮整个网络。TCP的拥塞避免算法用来防止这种情况的发生,它为速率的增
长安排了一个管理实体。

有了拥塞避免之后,在连接刚建立时,发送TCP会使用一个较小的拥塞窗口,它会限制
所能传输的未确认的数据数量。当发送者从对等TCP处接收到确认时,拥塞窗口在一开始时
会呈现指数级增长。但一旦拥塞窗口增长到一个被认为是接近网络传输容量的阈值时,其增长
速度就会变成线性,而不是指数级的。(对刚络容量的估算是根据检测到拥塞时的传输速率来
计算得出的或者在一开始建立连接时设定为一个固定值。)在任何时刻,发送TCP传输的数据
数量还会受到接收TCP的通告窗口和本地的TCP发送缓冲器的大小的限制。

慢启动和拥塞避免算法组合起来使得发送者可以快速地将传输速度提升至网络的可用容
量,并且不会超出该容量。这些算法的作用是允许数据传输快速地到达一个平衡状态,即发
送者传输包的速率与它从接收者处接收确认的速率一致。

在该图中,假定当cwnd为32个报文段时就会发生拥塞。于是设置ssthresh为16个报文段,
而cwnd为1个报文段。在时刻0发送了一个报文段,并假定在时刻1接收到它的ACK,此时
cwnd增加为2。接着发送了2个报文段,并假定在时刻2接收到它们的ACK,于是cwnd增加为4
(对每个ACK增加1次)。这种指数增加算法一直进行到在时刻3和4之间收到8个ACK后cwnd等
于ssthresh时才停止,从该时刻起,cwnd以线性方式增加,在每个往返时间内最多增加1个报
文段。

慢启动

当一个新的TCP连接建立或检测到由重传超时(RTO)导致的丢包时,需要执行慢启
动o TCP发送端长时间处于空闲状态也可能调用慢启动算法。慢启动的目的是,使TCP在
用拥塞避免探寻更多可用带宽之前得到cwnd值,以及帮助TCP建立ACK时钟。通常,
TCP在建立新连接时执行慢启动,直至有丢包时,执行拥塞避免算法(参见16.2.2节)进人
稳定状态。下文引自[RFC5681]:

在传输初始阶段,由于未知网络传输能力,需要缓慢探测可用传输资源,防止
短时间内大量数据注入导致拥塞。慢启动算法正是针对这一问题而设计。在数据传
输之初或者重传计时器检测到丢包后,需要执行慢启动。

TCP以发送一定数目的数据段开始慢启动(在SYN交换之后),称为初始窗口(Initial
Window,IW)。IW的值初始设为一个SMSS(发送方的最大段大小),但在[RFC5681]中设
为一个稍大的值,计算公式如下:

  • IW= 2* (SMSS)且小于等于2个数据段(当SMSS> 2190字节)
  • IW=3+(SMSS)且小于等于3个数据段(当2190≥SMSS> 1095字节)
  • IW= 4* (SMSS)且小于等于4个数据段(其他)

述IW的计算方式可能使得初始窗口为几个数据包大小(如3个或4个),为简单起
见,我们只讨论IW=1 SMSS的情况。TCP连接初始的cwnd=1 SMSS,意味着初始可用
窗口矽也为1 SMSS。注意到大部分情况下,SMSS为接收方的MSS(最大段大小)和路径
MTU(最大传输单元)两者中较小值。

假设没有出现丢包情况且每个数据包都有相应的ACK,第一个数据段的ACK到达,说
明可发送一个新的数据段。每接收到一个好的ACK响应,慢启动算法会以min (N, SMSS)
来增加cwnd值。这里的.Ⅳ是指在未经确认的传输数据中能通过这一“好的ACK”确认的字
节数。所谓的“好的ACK”是指新接收的ACK号大于之前收到的ACK。

因此,在接收到一个数据段的ACK后,通常cwnd值会增加到2,接着会发送两个数据
段。如果成功收到相应的新的ACK,cwnd会由2变4,由4变8,以此类推。一般情况下,
假设没有丢包且每个数据包都有相应ACK,在t轮后∥的值为矽=2k,即t= log2W,需要
t个RTT时间操作窗口才能达到矽大小。这种增长看似很快(以指数函数增长),但若与一
开始就允许以最大可用速率(即接收方通知窗口大小)发送相比,仍显缓慢。(矽不会超过
awnd。)

如果假设某个TCP连接中接收方的通知窗口非常大(比如说,无穷大),这时cwnd就是
影响发送速率的主要因素(设发送方有较大发送需求)。如前所述,cwnd会随着RTT呈指数
增长。因此,最终cwnd(矿也如此)会增至很大,大量数据包的发送将导致网络瘫痪(TCP
吞吐量与W/RTT成正比)。当发生上述情况时,cwnd将大幅度减小(减至原值一半)。这是
TCP由慢启动阶段至拥塞避免阶段的转折点,与cwnd和慢启动闽值(slow start threshold,
ssthresh)相关。

拥塞避免

如上所述,在连接建立之初以及由超时判定丢包发生的情况下,需要执行慢启动操作。
在慢启动阶段,cwnd会快速增长,帮助确立一个慢启动阈值。一旦达到阈值,就意味着可
能有更多可用的传输资源。如果立即全部占用这些资源,将会使共享路由器队列的其他连接
出现严重的丢包和重传情况,从而导致整个网络性能不稳定。

为了得到更多的传输资源而不致影响其他连接传输,TCP实现了拥塞避免算法。一旦确
立慢启动阈值,TCP会进入拥塞避免阶段,cwnd每次的增长值近似于成功传输的数据段大小。
这种随时间线性增长方式与慢启动的指数增长相比缓慢许多。

慢启动与拥塞避免的选择

在通常操作中,某个TCP连接总是选择运行慢启动和拥塞避免中的一个,不会出现两
者同时进行的情况。现在我们考虑,在任一给定时刻如何决定选用哪种算法。我们已经知
道,慢启动是在连接建立之初以及超时发生时执行的。那么决定使用慢启动还是拥塞避免的
关键因素是什么呢?

前面我们已经提到过慢启动阈值。这个值和cwnd的关系是决定采用慢启动还是拥塞避
免的界线。当cwnd< ssthresh,使用慢启动算法;当cwnd> ssthresh,需要执行拥塞避免;
而当两者相等时,任何一种算法都可以使用。由上面描述可以得出,慢启动和拥塞避免之间
最大的区别在于,当新的ACK到达时,cwnd怎样增长。有趣的是,慢启动阈值不是固定的,
而是随时间改变的。它的主要目的是,在没有丢包发生的情况下,记住上一次“最好的”操
作窗口估计值。换言之,它记录TCP最优窗口估计值的下界。

慢启动阈值的初始值可任意设定(如awnd或更大),这会使得TCP总是以慢启动状态开
始传输。当有重传情况发生,无论是超时重传还是快速重传,ssthresh会按下式改变:

ssthresh - max(在外数据值/2,2*SMSS) (16-1)

注意微软最近的(“下一代”)TCP/IP访议栈中,上述等式变为ssthresh=max (min (cwnd, awnd) /2, 2*SMSS)

我们已经知道,如果出现重传情况,TCP会认为操作窗口超出了网络传输能力范围。这
时会将慢启动阈值( ssthresh)减小至当前窗口大小的一半(但不小于2*SMSS),从而减小最
优窗口估计值。这样通常会导致ssthresh减小,但也有可能会使之增大。分析TCP拥塞避
免的操作流程,如果整个窗口的数据都成功传输,那么cwnd值可以近似增大1 SMSS。因
此,若cwnd在一段时间范围内已经增大,将ssthresh设为整个窗口大小的一半可能使其增
大。这种情况发生在当TCP探测到更多可用带宽时。在慢启动和拥塞避免结合的情况下,
ssthresh和cwnd的相互作用使得TCP拥塞处理行为显现其独有特性。下面我们探讨将两者
结合的完整的算法。