1、TCP计时器管理
- TCP 使用多个计时器(至少从概念上讲是计时器)来完成它的工作。其中最重要的是重传计时器(RTO, Retransmission Timeout )。当TCP 实体发出一个段时,它同时启动一个重传计时器。如果在该计时器超时前该段被确认,则计时器被停止。另一方面,如果在确认到来之前计时器超时,则段被重传(并且该计时器被重新启动)。于是问题就来了:超时间隔应该设为多长?
- 这个问题在传输层上比诸如802.1 1 那样的数据链路协议要更加困难。在数据链路层,期望的延迟是可以测量的毫秒数,并且是高度可预测的(即方差很小),所以,计时器可以被设置成恰好比期望的确认到达时间稍长即可,如图a所示。由于在数据链路层中确认极少被延迟(因为不存在拥塞),所以,如果在预期的时间内确认没有到来,则往往意味着帧或者确认己经被丢失了。(图a,数据链路层的确认到达时间的概率密度;图b,TCP确认到达时间的概率密度)
- TCP 面临着截然不同的环境。TCP 确认回到发送端所需时间的概率密度函数看起来更像图b,它更大也更可变。要想确定到达接收方的往返时间非常棘手,即使知道了这段时间,要确定超时间隔也非常困难。如果超时间隔设置得太短,比如说图b中的Tl ,则会发生大量不必要的重传,这么多无用数据包反而堵塞Internet;如果超时间隔被设置得太长(比如T2 ),则一旦数据包丢失之后,由于太长的重传延迟,所以性能会受到影响。而且,确认到达时间分布的均值和方差也会随着拥塞的发生或者解决而在几秒钟时间内迅速地改变。
- 解决方案是使用一个动态算法,它根据网络性能的连续测量情况,不断地调整超时间隔。TCP 通常采用的算法是CJacobson, 1988 )算法,其工作原理如下所述。对于每一个连接, TCP 维护一个变量SRTT( Smoothed Round-Trip Time,平滑的往返时间〉,它代表到达接收方往返时间的当前最佳估计值。当一个段被发送出去时, TCP 启动一个计时器,该计时器有两个作用,一是看该段被确认需要多长时间: 二是若确认时间太长,则触发重传动作。如果在计时器超时前确认返回,则TCP 测量这次确认所花的时间,比如说R。然后它根据下面的公式更新SRTT:SRTT =αSRTT + (1 一α) R。这里α 是一个平滑因子,它决定了老的RTT 值所占的权重。典型情况下α=7/8 。这类公式是指数加权移动平均(EWMA, Exponentially Weighted Moving Average ),或者丢弃样值中噪声的低通滤波器。
- 即使有了一个好的SRTT 值,要选择一个合适的重传超时间隔仍然不是一件容易的事情。在最初的实现中, TCP 使用2xRTT,但经验表明常数值太不灵活,当发生变化时它不能够很好地做出反应。尤其是,随机流量的排队模型(即泊松分布〉预测的结果是当负载接近容量时,延迟不仅变大,而且变化也大。这可能导致重传计时器超时而重新传输一个数据包的副本,尽管原先的数据包仍然在网络中传输着。这在高负载的情况下也有可能发生,最糟糕的是将额外的数据包发到网络中。
- 为了解决这个问题, Jacobson 提议让超时值对往返时间的变化以及平滑的往返时间要变得敏感。这种改变要求跟踪另一个平滑变量, RTTVAR(往返时间变化, Round-Trip TimeVARiation ),即更新为下列公式:RTTVAR =β RTTVAR + (1- β)| SRTT- R |。这就像以前的EWMA ,典型地,β= 3/4。 因此,重传超时值, RTO 为RTO = SRTT + 4 x RTTV AR。这里选择因子4 多少有点随意,但是乘以4 的操作用一个移位操作就可以实现,而且,所有数据包中大于标准方差4 倍以上的不足总数的1%。注意RTTVAR 并不是确切地等于标准方差(真正的它是平均方差),但它实际上足够接近。计算超时值的详细过程,包括变量的初始值设置,可参考盯C2988 。重传计时器的最小值为1 秒,无论估算值是多少。这是为了防止根据测量获得的欺骗性重传值而选择的保守值。
- 在采集往返时间的样值R 过程中有可能引发的一个问题是,当一个段超时并重新发送以后该怎么办?确认到达时,无法判断该确认是针对第一次传输,还是针对后来的重传。若猜测错误,则会严重影响重传超时值。Phil Karn 发现这个问题很艰难。Kam 是一名业余无线电爱好者,他对在一种极不可靠的无线介质上传输TCP/IP 数据包有浓厚的兴趣。他提出了一个很简单的建议: 不更新任何重传段的估算值。此外,每次连续重传的超时间隔值加倍,直到段能一次通过为止。这个修正算法称为Karn 算法( Karn 和阳tridge, 1987 )。大多数TCP 实现都采用了此算法。
- 重传计时器并不是TCP 使用的唯一计时器。第二个计时器是持续计时器( persistencetimer)。它的设计意图是为了避免出现以下所述的死锁情况。接收端发送一个窗口大小为0的确认,让发送端等一等。稍后,接收端更新了窗口,但是,携带更新消息的数据包丢失了。现在,发送端和接收端都在等待对方的进一步动作。当持续计时器超时后,发送端给接收端发送一个探询消息。接收端对探询消息的响应是将窗口大小告诉发送端。如果它仍然为0,则重置持续计时器,并开始下一轮循环。如果它非0,则现在可以发送数据了。有些TCP 实现使用了第三个计时器,即保活计时器( keepalive timer )。当一个连接空闲了较长一段时间以后,保活计时器可能超时,从而促使某一端查看另一端是否仍然还在。如果另一端没有响应,则终止连接。这个特性是有争议的,因为它增加了额外的开销,而且有可能由于瞬间的网络分区而终止掉一个本来很正常的连接。每个TCP 连接使用的最后一