TCP的超时与重传

一、引言

对于每个TCP连接,TCP管理4个不同的定时器

  1. 重传定时器用于当希望收到另一端的确认。
  2. 坚持 (persist) 定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。
  3. 保活 (keepalive) 定时器可检测到一个空闲连接的另一端何时崩溃或重启。
  4. 2MSL定时器测量一个连接处于TIME_WAIT状态的时间。

二、往返时间测量

TCP超时与重传中最重要的一部分是对一个给定连接,如何测量往返时间 (RTT)。由于路由器和网络流量均会变化,因此我们认为这个时间会经常变化,TCP应该跟踪这些变化并相应改变其超时时间。

2.1 方法一:根据估测RTT来确定重传时间

首先,TCP必须测量在发送一个带有特别序号的字节和接收到包含此字节的确认之间的RTT,用\(M\)表示所测量到的RTT。
最初TCP规范是TCP使用一个低通滤波器来更新一个被平滑的RTT估计器,记为\(R\)。 \[R \leftarrow \alpha R + (1-\alpha)M\] 这里\(\alpha\)是一个推荐值为0.9的平滑因子。RFC 793推荐重传超时时间RTO (Retransmission TimeOut) 的值如下: \[RTO = R\beta\] 这里的\(\beta\)是一个推荐值为2的时延离散因子。 综上,整个过程如下\[RTT\xrightarrow\alpha R \xrightarrow\beta RTO\] 当RTT变化范围很大时,这种方法无法跟上这种变化,从而引起不必要的重传。

2.2 跟踪RTT的方差和均值来计算重传时间

Jacobson指出,可以用均值偏差对标准偏差做逼近,并提出下面用于每个RTT测量\(M\)的公式。\[Err=M-A \\ A \leftarrow A+gErr\\ D \leftarrow D + h(|Err|-D \\ RTO=A+4D\] 其中\(A\)是被平滑的RTT,即均值的估计其,而\(D\)是被平滑的均值方差。\(A\)和\(D\)均被用于计算下一个重传时间 (RTO)。增量\(g\)起到平均作用,取值为1/8。偏差的增益是\(h\),取值为0.25。
当RTT变化较大时,较大的偏差增益将使得RTO快速上升。

2.3 Karn算法

当一个超时和重传发送时,在重传数据的确认最后达到时,不能更新RTT估计器,因为我们不知道ACK对应哪次传输。

2.4 RTT测量的一个例子

在时间10、14、21处的间隔是由在这些时刻附近发生重传引起的。Karn算法在另一个报文段被发送和确认事前组织我们更新估计器。

三、拥塞举例

用起始序号报文段发送时间做图,是一种较好的数据传输的可视化方法。如下图,可以看出在时刻10、14和21附近的3个重传。我们还可以看到在这3个点中只进行了一次报文段的重传,因为只有一个点下垂低于向上的斜率。

四、拥塞避免算法

拥塞避免算法是一种处理丢失分组的方法。
假定:分支收到损坏引起的丢失是非常少的(远小于1%),因此分组丢失意味着源主机和目的主机之间的某处网络发生了故障。
分组丢失的两种指示:发生超时以及收到重复的确认。
当拥塞发生时,拥塞避免算法希望降低分组进入网络的速率,于是调用慢启动来做到这一点。
拥塞避免算法和慢启动算法对每个连接维持两个变量:拥塞窗口\(cwnd\)和慢启动门限\(ssthresh\),工作过程如下:

  1. 对于给定的链接,初始化\(cwnd\)为1个报文段,\(ssthresh\)为65535字节
  2. TCP输出例程的输出不能超过\(cwnd\)和接收方通告窗口大小
  3. 拥塞发生时(超时或收到重复确认),\(ssthresh\)设置为当前窗口大小(\(cwnd\)和接收方通告窗口的最小值)的一半。此外,如果超时引起了拥塞,则\(cwnd\)被设置为1个报文段(这就是慢启动)。
  4. 新的数据被对方确认是,就增加\(cwnd\)。增加的方法依赖于是否进行慢启动或拥塞避免。
    如果\(cwnd\)小于或等于\(ssthresh\),则认为在执行慢启动,慢启动一直持续到我们回到当拥塞发生时所处的位置一半时停止。慢启动算法初始设置\(cwnd\)为1个报文段,此后每收到一个确认就加1。这会使窗口按指数方式增长。
    否则,正在进行拥塞避免,每收到一个确认将\(cwnd\)增加\(1/cwnd\),这是一个加性增长。

    五、快速重传与快速恢复算法

    如果一连串收到3个或3个以上重复的ACK,就非常可能是一个报文段丢失了。于是我们就重传丢失的数据报文段,而无需等待超时定时器溢出。这就是快速重传算法。
    接下来执行的不是慢启动算法而是拥塞避免算法,这就是快速重传算法。不执行慢启动的原因是重复的ACK不仅仅告诉我们一个分组丢失了。由于接收方只有收到另一个报文段时才会产生重复的ACK,此时这个报文段已经离开了网络并进入了接收方缓存。也就是说在收发两端之间仍然有流动的数据,因此不想执行慢启动来减少数据流。
    算法流程如下:

    1. 收到第3个重复的ACK时,把\(ssthresh\)设置为当前拥塞窗口\(cwnd\)的一半。重传丢失的报文段。设置\(cwnd\)为\(ssthresh\)加上3倍的报文段大小。
    2. 每次收到另一个重复的ACK时,\(cwnd\)增加一个报文段大小并发送一个分组(如果新的cwnd运行发送)。
    3. 收到下一个确认新数据ACK到达时,设置\(cwnd\)为\(ssthresh\)(第一步设置的值)。这一步采用的是拥塞避免,因为分组丢失时,我们把当前的速率减半。

posted on 2017-03-19 22:02  花老🐯  阅读(629)  评论(0编辑  收藏  举报

导航