21.6 拥塞避免算法
在第2 0 . 6节介绍的慢启动算法是在一个连接上发起数据流的方法,但有时我们会达到中间路由器的极限,此时分组将被丢弃。拥塞避免算法是一种处理丢失分组的方法。该方法的具体描述见 [Jacobson 1988]。
该算法假定由于分组受到损坏引起的丢失是非常少的(远小于 1 %),因此分组丢失就意味着在源主机和目的主机之间的某处网络上发生了拥塞。有两种分组丢失的指示:发生超时和接收到重复的确认(我们在 2 1 . 5节看到这种现象。如果使用超时作为拥塞指示,则需要使用一个好的RT T算法,正如在2 1 . 3节中描述的那样)。
拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法通常在一起实现。
拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口 c w n d和一个慢启动门限s s t h re s h。这样得到的算法的工作过程如下:
- 对一个给定的连接,初始化 c w n d为1个报文段,s s t h re s h为6 5 5 3 5个字节。
- TCP输出例程的输出不能超过 c w n d和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。
- 当拥塞发生时(超时或收到重复确认),s s t h re s h被设置为当前窗口大小的一半( c w n d和接收方通告窗口大小的最小值,但最少为 2个报文段)。此外,如果是超时引起了拥塞,则c w n d被设置为1个报文段(这就是慢启动)。
- 当新的数据被对方确认时,就增加 c w n d,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果 c w n d小于或等于s s t h re s h,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤 2中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。
慢启动算法初始设置 c w n d为1个报文段,此后每收到一个确认就加 1。正如2 0 . 6节描述的那样,这会使窗口按指数方式增长:发送 1个报文段,然后是2个,接着是4个……。
拥塞避免算法要求每次收到一个确认时将 c w n d增加1 /c w n d。与慢启动的指数增加比起来,这是一种加性增长(additive increase)。我们希望在一个往返时间内最多为 c w n d增加1个报文段(不管在这个 RT T中收到了多少个 A C K),然而慢启动将根据这个往返时间中所收到的确认的个数增加c w n d。
所有的4 . 3 B S D版本和4 . 4 B S D都在拥塞避免中将增加值不正确地设置为 1个报文段的一小部分(即一个报文段的大小除以 8),这是错误的,并在以后的版本中不再使用[Floyd 1994]。但是,为了和(不正确的)实现的结果对应,我们在将来的计算中给出了这个细节。
在[Leffler et al. 1989]中介绍的4.3BSD Tahoe版本仅在对方处于一个不同的网络上时才进行慢启动。而4.3BSD Reno版本改变了这种做法,因此,慢启动总是被执行。图2 1 - 8是慢启动和拥塞避免的一个可视化描述。我们以段为单位来显示 c w n d和s s t h re s h,但它们实际上都是以字节为单位进行维护的。
在该图中,假定当 c w n d为3 2个报文段时就会发生拥塞。于是设置 s s t h re s h为1 6个报文段,而c w n d为1个报文段。在时刻 0发送了一个报文段,并假定在时刻 1接收到它的 A C K,此时c w n d增加为2。接着发送了2个报文段,并假定在时刻 2接收到它们的A C K,于是c w n d增加为4(对每个A C K增加1次)。这种指数增加算法一直进行到在时刻 3和4之间收到8个A C K后c w n d等 于s s t h re s h时才停止,从该时刻起, c w n d以线性方式增加,在每个往返时间内最多增加 1个报文段。
正如我们在这个图中看到的那样,术语“慢启动”并不完全正确。它只是采用了比引起拥塞更慢些的分组传输速率,但在慢启动期间进入网络的分组数增加的速率仍然是在增加的。只有在达到s s t h re s h拥塞避免算法起作用时,这种增加的速率才会慢下来。