【TCP/IP详解 卷一:协议】第二十一章 TCP的超时与重传

作为TCP的重头戏,本章节涉及了许多关于计算方面的内容,使用了大量的例子来指明一些观点。
我使用的理解方法是:通过别人的博客,以及实例结合进行理解,不然会很吃力。

21.1 引言 reliable

TCP提供可靠的运输层,它使用的方法之一就是确认从另外一端收到的数据。
也就是说通过ACK,或者说 TCP自时钟,来保证这一特性。
TCP通过在发送的时候,设置一个定时器来解决这些问题。如果当定时器溢出还没有收到确认,就重传数据,对于任何的实现而言,关键就在超时和重传的策略。

四个不同的定时器

重传定时器:使用于 当希望收到另外一端的确认。 -与本章有关
坚持定时器:使窗口信息不断流动。
保活(keep-alive)定时器:检测一个空闲的连接的另外一端何时崩溃或重启。
2MSL定时器:用于测量一个连接处于 TIME_WAIT 状态的时间。

我的小概况

首先介绍的是,RTT和RTO的计算,当成功接收到确认的时候,使用公式法,当没有接收到而超时的时候,采用指数避退法。
然后接入慢启动和拥塞逼退算法,也就是 避免网络拥塞 TCP所采取的算法。还有旁带介绍了快速启动和快速恢复算法。
抓住两个关键的英文单词:ssthresh慢启动门限,cwnd拥塞窗口。

最后一笔带过了 之前提及的路由度量 还有 TCP如何处理ICMP差错,以及重新分组的内容。前面的两块内容相比之下更需要理解 和掌握。

第一部分:超时

21.2 & 21.3 RTT 和 RTO 的内容

当没有接收到ACK的时候的策略:指数退避

当发送方没有收到 由接收方对数据的 ACK 的时候,RTO 使用的是 指数退避策略:重传时间RTO 在重传定时器每一次的超时溢出之后 都翻一倍,直到64s。
记住这个前提,如果是接收到了ACK的话,有可能会对RTO进行更新(超时并重传一次之后 接收到的话有可能会造成二义性,所以不进行更新),那么使用的是公式更新RTO的方法。

当接收到ACK的时候(正常情况)的策略:公式法(均值和方差法) RTT估计器

TCP超时与重传中 最重要的部分 就是 对一个给定连接的往返时间(RTT)的测量。这个时间会经常发生变化,所以TCP必须对它进行跟踪。
首先 TCP 必须测量在发生一个 带有特别序号 的字节 和 接收包含该字节的确认 之间的RTT。

最初的公式

最初的TCP规范 使TCP使用 低通过滤器 来更新一个 被平滑的RTT估计器(记为R)
R <- aR + (1-a)M 这里的 a 为一个推荐值为0.9的 平滑因子。M是新测量的RTT。
RTO = Rβ β 是一个推荐值为2的 时延离散因子。

也就是说,这个公式 是根据一个估计器(R) 来计算 RTO 的。估计器的90% 来自前面的估计器,10%来自新测量的RTT。

新的公式(Jacobson) 均值偏差 -减小误差

在RTT变化很快的情况之下,以前旧的公式无法跟上这种变化,会引起不必要的重传。

新的公式根据两个东西来计算:(1)RTT估计器 (2)均值与偏差(D) 最大程度的减小了误差。

在往返时间变化起伏很快的情况之下,基于 均值和方差 来计算RTO,能够提供更好的相应。

Err <- M(测量的RTT) - A(之前的RTT估计器)
A <- A + g(1/8 即 0.125) * Err
D <- D + h(偏差的增益 取0.25) * [|Err| - D]
RTO = A + 4D

相关的内容参见教材P228。
按我自己的理解 这些新加入的 比如偏差的增益 平均的g 都是为了尽可能地减小RTO的误差。
均值偏差 是对标准偏差的一种好的逼近,但却更容易进行计算。

顺便一提的是,g,h 和倍数4 都是2的倍数,这也是Jacobson的设计:计算均可通过 移位操作

Karn算法 -解决重传二义性问题

当一个分组发送之后发生了超时,RTO根据之前的 指数退避 增长,分组以更长的RTO进行重传。然后收到了一个ACK。
那么这个ACK是针对第一个分组(超时的)的 还是针对第二个分组的(重传的)?

Karn 规定,当一个重传和超时发送的时候,在重传数据的确认最后到达的时候,不能更新RTT估计器,因为我们不知道ACK对应哪个数据报:是之前超时的呢?还是重传的呢?
此时RTO已经因为超时而得到了更新,下次传输的时候使用这个更新的RTO。

21.4 往返时间RTT的例子

并不是所有发送的数据报都被计时:在发送一个报文段的时候,如果给定连接的定时器已经被使用,则该报文段不被计时。

每次调用 500msTCP定时器例程 的时候,都会增加一个计数器来完成计时。
根据教材中的例子和我个人的理解,这个计时器(或者说时钟滴答),是在特定的时间计数的。比如 我在 0s 发送了一个数据报,在 1.061s 接收到了这个数据报的ACK,那么由于在这个过程中,计数器分别在 0.03s,0.53s,1.03s 进行了时钟滴答(第一个滴答一定在 0-0.061s 之间),所以一共是 三个时钟滴答。
在随后的RTO计算的时候,这个计数器得到的时钟滴答,用于计算往返时间:也就是上面新公式的M。

相关计算 参照教材 P232. 需要说明的一点是,初始化的时候:RTT估计器 A = 0,偏差的增益 D = 3s,RTO = A + 2D。

第二部分:重传

重头戏:TCP应对网络拥塞的策略

Review

流量控制:发送方-cwnd拥塞窗口 接收方-rwnd通告窗口
本章节主要和 cwnd 有关。

防止网络拥塞:慢启动 ssthresh 拥塞避免算法

本章主要介绍 TCP如何实现流量控制和避免网络拥塞

21.5 & 21.6 慢启动 与 拥塞避免算法

这一部分的知识点,可以参考:TCP慢启动、拥塞避免、快速重传、快速回复 归纳的非常详细,通俗易懂。

简单的来说,慢启动 和 拥塞避免算法 实现的内容基本都差不多,只不过 慢启动 是接收到一个ACK 就让 cwnd +1,而 拥塞避免 是接收到发送的所有数据报的 ACK 才让 cwnd +1。
慢启动 一点也不慢,它对 拥塞窗口cwnd 采用的是指数型增长,而 拥塞避免 是为了防止 慢启动后期 cwnd增长的速度过快,把这个速度控制下来,避免造成拥塞。拥塞避免 采取的是 加法增长。

区别 慢启动 和 拥塞避免 的界限是 ssthresh慢启动门限,当 cwnd(计数单位为数据段) 的值 x MSS = 总的字节数 > ssthresh(一般为65535字节) 的时候,停止慢启动,启动 拥塞避免。当前者小于后者的时候 也就是开始阶段 使用的是 慢启动。

慢启动 和 拥塞避免 算法 假定:当分组丢失的时候,意味着 在源主机和目的主机之间的网络 发送了拥塞。(由于分组受到损害而引起的丢失 是非常少的(<<1%))

当拥塞发生的时候,我们希望降低分组进入网络的传输速率,所以我们采用了慢启动,在实际中 慢启动 和 拥塞避免 一般在一起实现。

算法概要 -教材P235:

1.对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。

2.TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用 的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。

3.当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半(cwnd 和接收方通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则 cwnd被设置为1个报文段(这就是慢启动)。

4.当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。 慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤2 中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。 

21.7 快速重传 和 快速恢复

当TCP连续收到三个重复的ACK的时候,就假定一个报文段已经丢失并且重传自那个序号起的一个报文段。这就是快速重传算法。
如果接下来执行的不是 慢启动算法 而是 拥塞避免算法,这就是快速恢复算法。没有执行慢启动的原因:收到重复的ACK不仅仅告诉我们一个分组丢失了,由于接收方只有在收到一个报文段的时候才会返回ACK,所以 在收发两端之间 还有流动的数据,我们不希望执行慢启动来突然减少数据流。

算法概要P237:

1.当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的 报文段。设置cwnd为ssthresh加上3倍的报文段大小。

2.每次收到另一个重复的ACK时, cwnd增加1个报文段大小并发送1个分组(如果新的 cwnd允许发送)。

3.当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个 ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该 是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥 塞避免,因为当分组丢失时我们将当前的速率减半。 

最终,拥塞窗口将会超过接收方的通告窗口rwnd,意味着通告窗口将对数据流进行限制。

第三部分:其他内容

21.9 按每条路由进行度量

在介绍IP协议的时候,介绍了度量这个概念,当时是以跳数作为度量。
现在可以将 TCP的超时与重传 的相关信息保存在路由表项中:

  • 被平滑的 RTT 估计器
  • 被平滑的 均值偏差
  • ssthresh慢启动门限
  • MT
  • 输出的带宽时延乘积
  • 输入的带宽时延乘积

21.10 ICMP的差错 如何处理

一个接受到的 源站抑制ICMP差错:拥塞窗口cwnd 设置为1,启动 慢启动,ssthresh不变。
接受到的 主机不可达 或者 网络不可达:被忽略。因为这些都被认为是短暂现象,有可能是中间路由关闭了。TCP试图发送引起该差错的数据,尽管有可能超时。

21.11 重新分组

当TCP超时且重传的时候,它不一定要重传相同的报文段,相反 TCP允许重新分组而发送一个较大的报文段,有助于提高性能。因为 TCP是使用字节序号,而不是使用报文段序号 来识别发送的数据 和进行确认。

2016/8/18

原文地址:https://www.cnblogs.com/qq952693358/p/5782076.html