TCP拥塞控制方法 cyrus
1999年公布的因特网建议标准RFC2581定义了进行拥塞控制的四种算法,即慢开始(slow-start),拥塞避免(congestion avoidance),快重传(fast retransmit)和快恢复(fast recovery).以后RFC2582和RFC3390又对这些算法进行了一些改进.下面就介绍这些算法的原理.
为了集中精力讨论拥塞控制,我们假定:
(1)数据是单方向传送,而另一个方向只传送确认.
(2)接收方总是有足够大的缓存空间,因而发送穿口的大小由网络的拥塞程度来决定.
1,慢开始和拥塞避免
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量.拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化.发送方让自己的发送窗口等于拥塞窗口.以后我们就知道,如果再考虑到接收方的接收能力,那么发送窗口还可能小于拥塞窗口.
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去.但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数.
发送方又是如何知道网络发生了拥塞呢?我们知道,当网络发生拥塞时,路由器就要丢弃分组.因此只要发送方没有按时收到应当到达的确认报文,就可以猜想网络可能出现了拥塞.现在通信线路的传输质量一般都很好,因传输出差错而丢弃分组的概率是很小的(远小于1%).
下面将讨论拥塞窗口cwnd的大小是怎样变化的.我们从慢开始算法讲起.
慢开始算法的思路是这样的.当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况.经验证明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值.通常在刚刚开始发送报文段时,先把拥塞窗口cwnd设置为一个最大报文段MSS的数值.而在每收到一个对新的报文段的确认后,把拥塞窗口增加到多一个MSS的数据.用这样的方法逐步增大发送方的拥塞窗口cwnd,可以使分组注入到网络的速率更加合理.
下面用例子说明慢开始算法的原理.为方便起见,我们用报文段的个数作为窗口大小的单位(请注意,实际上TCP是用字节作为窗口的单位),这样可以使用较小的数字来说明拥塞控制的原理.
在一开始发送方先设置cwnd=1,发送第一个报文段M1,接收方收到后确认M1.发送方收到对M1的确认后,把cwnd从1增大到2,于是发送方接着发送M2和M3两个报文段.接收方收到后发回对M2和M3的确认.发送方每收到一个对新报文段的确认(重传的不算在内)就使发送方的拥塞窗口加1,因此发送方在收到两个确认后,cwnd就从2增大到4,并可发送M4~M7共4个报文段(见图1).因此使用慢开始算法后,每经过一个传输轮次(transmission round),拥塞窗口cwnd就加倍.
这里我们使用了一个名词----传输轮次.从图1可以看出,一个传输轮次所经历的时间其实就是往返时间RTT.不过使用"传输轮次"更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认.例如,拥塞窗口cwnd的大小是4个报文段,那么这时的往返时间RTT就是发送方连续发送4个报文段,并收到这4个报文段的确认,总共经历的时间.
我们还要指出,慢开始的"慢"并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd.这当然比按照大的cwnd一下子把许多报文段突然注入到网络中要"慢得多".这对防止网络出现拥塞是一个非常有力的措施.
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量(如何设置ssthresh,后面还要讲)。慢开始门限ssthresh的用法如下:
当cwnd<ssthresh时,使用上述的慢开始算法;
当cwnd>ssthresh时,停止使用慢开始算法而改用拥塞避免算法;
当cwnd=ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法。
拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1【注释】,而不是加倍.这样,拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认),就要把慢开始门限ssthresh设置为出现拥塞时的发送方窗口值的一半(但不能小于2)【注释】。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法.这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
图2用具体数值说明了上述拥塞控制的过程。现在发送窗口的大小和拥塞窗口一样大。
-------------------------------------------------------------------------
注释:
1,RFC2581规定在一开始cwnd应设置为不超过2xMSS个字节,并且在一开始也不能超过两个报文段。但通常就将cwnd设置为一个MSS。
2,请注意,因为现在是讲原理,把窗口的单位改为报文段的个数。实际上应当是”拥塞窗口仅增加一个MSS的大小,单位是字节“,在具体实现拥塞避免算法的方法可以这样来完成。只要收到一个新的确认,就使拥塞窗口cwnd增加(MSSxMSS/cwnd)个字节.例如,假定cwnd等于10个MSS的长度,而MSS是1460字节.发送方可一连发送14600字节(即10个报文段).假定接收方每收到一个报文段就发同一个确认.于是发送方每收到一个新的确认,就把拥塞窗口稍微增大一些,即增大0.1MSS=146字节.经过一个往返时间RTT(或一个传输轮次)后,发送方共收到10个新的确认,拥塞窗口就增大了1460字节,正好是一个MSS的大小.
3,RFC2581给出了根据已发送出但还未被确认的数据字节数来设置ssthresh的新的计算公式.但在讨论拥塞控制原理时,我们就采用这种把问题简化的方法来表述.
--------------------------------------------------------------------------
(1)当TCP连接进行初始化时,把拥塞窗口cwnd置为1.前面已说过,为了便于理解,图中的窗口单位不使用字节而使用报文段的个数.慢开始门限的初始值设置为16个报文段,即ssthresh=16.
(2)在执行慢开始算法时,拥塞窗口cwnd的初始值为1.以后发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1,然后开始下一轮的传输(请注意,图2的横坐标是传输轮次).因此拥塞窗口cwnd随着传输轮次按指数规律增长.当拥塞窗口cwnd增长到慢开始门限值ssthresh时(即当cwnd=16时),就改为执行拥塞避免算法,拥塞窗口按线性规律增长.
(3)假定拥塞窗口的数值增长到24时,网络出现超时(这很可能就是网络发生拥塞了).更新后的ssthresh值变为12(即变为出现超时的拥塞窗口数值24的一半,拥塞窗口再重新设置为1,并执行慢开始算法.当cwnd=ssthresh=12时改为执行拥塞避免算法,拥塞窗口按线性规律增长,每经过一个往返时间增加一个MSS的大小.
在TCP拥塞控制的文献中经常可看见"乘法减小"(Multiplicative Decrease)和"加法增大"(Additive Increase)这样的提法."乘法减小"是指不论在慢开始阶段还是拥塞避免阶段,只要出现超时(即很可能出现了网络拥塞),就把慢开始门限值ssthresh减半,即设置为当前的拥塞窗口的一半(与此同时,执行慢开始算法).当网络频繁出现拥塞时,ssthresh值就下降得很快,以大大减少注入到网络中的分组数.而"加法增大"是指执行拥塞避免算法后,使拥塞窗口缓慢增大,以防止网络过早出现拥塞.上面两种算法合起来常称为AIMD算法(加法增大乘法减小).对这种算法进行适当修改后,又出现了其他一些改进的算法.但使用最广泛的还是AIMD算法.
这里要再强调一下,"拥塞避免"并非指完全能够避免了拥塞.利用以上的措拖要完全避免网络拥塞还是不可能的,