您的位置:首页 > 理论基础 > 计算机网络

第21章 TCP的超时与重传

2018-11-11 22:09 4849 查看

21.1 引言

TCP提供可靠的运输层。它使用的方法之一就是确认从另一端收到的数据。但数据和确认都有可能会丢失。TCP通过在发送时设置一个定时器来解决这种问题。如果当定时器溢出时还没有收到确认,它就重传该数据。对任何实现而言,关键之处就在于超时和重传的策略,即怎样决定超时间隔和如何确定重传的频率。

我们已经看到过两个超时和重传的例子:(1)在6.5节的ICMP端口不能到达的例子中,看到TFTP客户使用UDP实现了一个简单的超时和重传机制:假定5秒是一个适当的时间间隔,并每隔5秒进行重传;(2)在向一个不存在的主机发送ARP的例子中(第4.5节),我们看到当TCP试图建立连接的时候,在每个重传之间使用一个较长的时延来重传SYN。

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

  1. 重传定时器使用于当希望收到另一端的确认。在本章我们将详细讨论这个定时器以及一些相关的问题,如拥塞避免。
  2. 坚持(persist)定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。第22章将讨论这个问题。
  3. 保活(keepalive)定时器可检测到一个空闲连接的另一端何时崩溃或重启。第23章将描述这个定时器。
  4. 2MSL定时器测量一个连接处于TIME_WA IT状态的时间。我们在18.6节对该状态进行了介绍。

本章以一个简单的TCP超时和重传的例子开始,然后转向一个更复杂的例子。该例子可以使我们观察到TCP时钟管理的所有细节。可以看到TCP的典型实现是怎样测量TCP报文段的往返时间以及TCP如何使用这些测量结果来为下一个将要传输的报文段建立重传超时时间。接着我们将研究TCP的拥塞避免—当分组丢失时TCP所采取的动作—并提供一个分组丢失的实际例子,我们还将介绍较新的快速重传和快速恢复算法,并介绍该算法如何使TCP检测分组丢失比等待时钟超时更快。

21.2 超时与重传的简单例子

首先观察TCP所使用的重传机制,我们将建立一个连接,发送一些分组来证明一切正常,然后拔掉电缆,发送更多的数据,再观察TCP的行为。

第21章 TCP的超时与重传227

图21-1 TCP超时和重传的简单例子

第1、2和3行表示正常的TCP连接建立的过程,第4行是“hello,world”(12个字符加上回车和换行)的传输过程,第5行是其确认。接着我们从svr4拔掉了以太网电缆,第6行表示“and hi”将被发送。第7~18行是这个报文段的12次重传过程,而第19行则是发送方的TCP最终放弃并发送一个复位信号的过程。

现在检查连续重传之间不同的时间差,它们取整后分别为1、3、6、12、24、48和多个64秒。在本章的后面,我们将看到当第一次发送后所设置的超时时间实际上为1.5秒(它在首次发送后的1.0136秒而不是精确的1.5秒后,发生的原因我们已在图18-7中进行了解释),此后该时间在每次重传时增加1倍并直至64秒。

这个倍乘关系被称为“指数退避(exponential backoff)”。可以将该例子与6.5节中的TFTP例子比较,在那里每次重传总是在前一次的5秒后发生。

首次分组传输(第6行,24.480秒)与复位信号传输(第19行,566.488秒)之间的时间差约为9分钟,该时间在目前的TCP实现中是不可变的。

对于大多数实现而言,这个总时间是不可调整的。Solaris 2.2允许管理者改变这个时间(E.4节中的tcp_ip_abort_interval变量),且其默认值为2分钟,而不是最常用的9分钟。

21.3 往返时间测量

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

首先TCP必须测量在发送一个带有特别序号的字节和接收到包含该字节的确认之间的RTT。在上一章中,我们曾提到在数据报文段和ACK之间通常并没有一一对应的关系。在图20.1中,这意味着发送方可以测量到的一个RT T,是在发送报文段4(第1~1024字节)和接收报文段7(对1~1024字节的ACK)之间的时间,用M表示所测量到的RTT。

228TCP/IP详解,卷1:协议

这里的α是一个推荐值为0.9的平滑因子。每次进行新测量的时候,这个被平滑的RTT将得到更新。每个新估计的90%来自前一个估计,而10%则取自新的测量。

该算法在给定这个随RT T的变化而变化的平滑因子的条件下,RFC 793推荐的重传超时时间RTO(Retransmission TimeOut)的值应该设置为

这里的β是一个推荐值为2的时延离散因子。

[Jacobson 1988]详细分析了在RTT变化范围很大时,使用这个方法无法跟上这种变化,从而引起不必要的重传。正如Jacobson记述的那样,当网络已经处于饱和状态时,不必要的重传会增加网络的负载,对网络而言这就像在火上浇油一样。

除了被平滑的RTT估计器,所需要做的还有跟踪RT T的方差。在往返时间变化起伏很大时,基于均值和方差来计算RTO,将比作为均值的常数倍数来计算RTO能提供更好的响应。在[Jacobson 1988] 中的图5和图6中显示了根据RFC 793计算的某些实际往返时间的RTO和下面考虑了往返时间的方差所计算的RTO的比较结果。

正如Jacobson所描述的,均值偏差是对标准偏差的一种好的逼近,但却更容易进行计算(计算标准偏差需要一个平方根)。这就引出了下面用于每个RTT测量M的公式。

这里的A是被平滑的RTT(均值的估计器)而D则是被平滑的均值偏差。Err是刚得到的测量结果与当前的RTT估计器之差。A和D均被用于计算下一个重传时间(RTO)。增量g起平均作用,取为1/8(0.125)。偏差的增益是h,取值为0.25。当RTT变化时,较大的偏差增益将使RTO快速上升。

[Jacobson 1988]指明在计算RTO时使用2D,但经过后来更深入的研究,[Jacobson1990c]将该值改为4D,也就是在BSD Net/1的实现中使用的那样。

Jacobson指明了一种使用整数运算来计算这些公式的方法,并被许多实现所采用(这也就是g,h和倍数4均是2的乘方的一个原因,这样一来计算均可只通过移位操作而不需要乘、除运算来完成)。

将Jacobson与最初的方法比较,我们发现被平滑的均值计算公式是类似的(α是1减去增益g),而增益可使用不同的值。而且Jacobson计算RTO的公式依赖于被平滑的RT T和被平滑的均值偏差,而最初的方法则使用了被平滑的RT T的一个倍数。

在看完下一节中的例子时,我们将看到这些估计器是如何被初始化的。

Karn算法

在一个分组重传时会产生这样一个问题:假定一个分组被发送。当超时发生时,RTO正如21.2节中显示的那样进行退避,分组以更长的RTO进行重传,然后收到一个确认。那么这个ACK是针对第一个分组的还是针对第二个分组呢?这就是所谓的重传多义性问题。

第21章 TCP的超时与重传229

图21-2 分组交换和RTT测量

在每次调用500 ms的TCP的定时器例程时,就增加一个计数器来完成计时。这意味着,如果一个报文段的确认在它发送550 ms后到达,则该报文段的往返时间RT T将是1个滴答(即500 ms)或是2个滴答(即1000 ms)。

对每个连接而言,除了这个滴答计数器,报文段中数据的起始序号也被记录下来。当收到一个包含这个序号的确认后,该定时器就被关闭。如果ACK到达时数据没有被重传,则被平滑的RTT和被平滑的均值偏差将基于这个新测量进行更新。

图21-2中连接上的定时器在发送报文段1时启动,并在确认(报文段2)到达时终止。尽管它的RTT是1.061秒(tcpdump的输出),但插口排错的信息显示该过程经历了3个TCP时钟滴答,即RTT为1500 ms。

下一个被计时的是报文段3。当2.4ms后传输报文段4时,由于连接的定时器已经被启动,因此该报文段不能被计时。当报文段5到达时,确认了正在被计时的数据。虽然我们从tcpdump的输出结果可以看到其RTT是0.808秒,但它的RTT被计算为1个滴答(500 ms)。

定时器在发送报文段6时再次被启动,并在1.015秒后接收到它的确认(报文段10)时终止。测量到的RTT是2个滴答。报文段7和9不能被计时,因为定时器已经被使用。而且,当收到报文段8(第769字节的确认)时,由于该报文段不是正在计时的数据的确认,因此什么也没有进行更新。

第21章 TCP的超时与重传231

图21-3 RTT测量和时钟滴答

在图的上端表示间隔为500 ms的时钟滴答,图的下端表示tcpdump的输出时间及定时器何时被启动和关闭。在发送报文段1和接收到报文段2之间经历了3个滴答,时间为1.061秒,因此假定第1个滴答发生在0.03秒处(第1个滴答一定在0~0.061秒之间)。接着该图表示了第2个被测量的RTT为什么被记为1个滴答,而第3个被记为2个滴答。

在这个完整的例子中,128个报文段被传送,并收集了18个RTT采样。图21-4表示了测量的RTT(取自tcpdump的输出)和TCP为超时所使用的RTO(取自插口排错的输出)。在图21-2中,x轴从时间0开始,表示的是传输报文段1的时刻,而不是传输第1个SYN的时刻。

图21-4 测量出的RTT和TCP计算的RTO的例子

测量出RTT的前3个数据点对应图21-2所示的3个RTT。在时间10,14和21处的间隔是由在这些时刻附近发生的重传(将在本章后面给出)引起的。Karn算法在另一个报文段被发送和确认之前阻止我们更新估计器。同样注意到在这个实现中,TCP计算的RTO总是500 ms的倍数。

21.4.2 RTT估计器的计算

让我们来看一下RTT估计器(平滑的RT T和平滑的均值偏差)是如何被初始化和更新,以及每个重传超时是怎样计算的。

变量A和D分别被初始化为0和3秒。初始的重传超时使用下面的公式进行计算

232TCP/IP详解,卷1:协议

图21-5 初始SYN的超时和重传

当超时在5.802秒后发生时,计算当前的RTO值为

RTO = A + 4D = 0 + 4 × 3 = 12s

因此,应用于RTO的指数退避取为12。由于这是第1次超时,我们使用倍数2,因此下一个超时时间取值为24秒。再下一个超时时间的倍数为4,得出值为48秒(这些初始RTO,对于一个连接上的最初的SYN,取值为6秒,接下来为24秒,正是我们在图4-5中看到的)。

ACK在重传后467ms到达。A和D的值没有被更新,这是因为Karn算法对重传的处理比较模糊。下一个发送的报文段是第4行的ACK,但它只是一个ACK,所以没有被计时(只有数据报文段才会被计时)。

当发送第1个数据报文段时(图21-2中的报文段1),RTO没有改变,这同样是由于Karn算法。当前的24秒一直被使用,直到进行一个RTT测量。这意味着图21-4中时间0的RTO并不真的是24,但我们没有画出那个点。

当第1个数据报文段的ACK(图21-2中的报文段2)到达时,经历了3个时钟滴答,估计器被初始化为

(因为经历3个时钟滴答,因此,M取值为1.5)。在前面,A和D初始化为0,RTO的初始计算值为3。这是使用第1个RTT的测量结果M对估计器进行首次计算的初始值。计算的RTO值为

当第2个数据报文段的ACK(图21-2中的报文段5)到达时,经历了1个时钟滴答(0.5秒),估计器按如下更新:

Err、A和D的定点表示与实际使用的定点计算(在简化浮点计算中表示过)有一些微小的差别。这些不同使RTO取值为6秒(而非6.3125秒),正如我们在图21-4中的时间1.871处所画的那样。

第21章 TCP的超时与重传233

图21-6 从slip发送32768个字节的数据到vangogh

可以立即看到图21-6中发生在时刻10,14和21附近的3个重传。我们还可以看到在这3个点中只进行了一次报文段的重传,因为只有一个点下垂低于向上的斜率。

仔细检查一下这几个下垂点中的第1个点(在10秒标记处的附近)。整理tcpdump的输出结果可以得到图21-7。

在这个图中,除了下面将要讨论的报文段72,已经去掉了其他所有的窗口通告。主机slip总是通告窗口大小为4096,而主机vangogh则通告窗口为8192。该图中报文段的编号可以看作是图21-2的延续,在那里报文段的编号从1开始。与图21-2一样,报文段根据在slip上发送和接收的顺序进行编号,tcpdump在主机slip上运行。我们还去掉了一些与讨论无关的段(第44,47和49以及所有来自vangogh的ACK)。

234TCP/IP详解,卷1:协议

图21-7 10秒标记处附近重传的分组交换

看来报文段45丢失或损坏了,这一点无法从该输出上进行辨认。能够在主机slip上看到的是对第6657字节(报文段58)以前数据的确认(不包括字节6657在内)。紧接着的是带有相同序号的8个ACK。正是接收到报文段62,也就是第3个重复ACK,才引起自序号6657开始的数据报文段(报文段63)进行重传。的确,源于伯克利的TCP实现对收到的重复ACK进行计数,当收到第3个时,就假定一个报文段已经丢失并重传自那个序号起的一个报文段。这就是Jacobson的快速重传算法,该算法通常与他的快速恢复算法一起配合使用。我们在第21.7节中介绍这两个算法。

注意到在重传后(报文段63),发送方继续正常的数据传输(报文段67、69和71)。TCP不需要等待对方确认重传。

现在检查一下在接收端发生了什么。当按序收到正常数据(报文段43)后,接收TCP将255个字节的数据交给用户进程。但下一个收到的报文段(报文段46)是失序的:数据的开始序号(6913)并不是下一个期望的序号(6657)。TCP保存256字节的数据,并返回一个已成功接收数据的最大序号加1(6657)的ACK。被vangogh接收到的后面7个报文段(48,50,52,54,55,57和59)也是失序的,接收方TCP保存这些数据并产生重复ACK。

第21章 TCP的超时与重传235

图21-8 慢启动和拥塞避免的可视化描述

在该图中,假定当cwnd为32个报文段时就会发生拥塞。于是设置ssthresh为16个报文段,而cwnd为1个报文段。在时刻0发送了一个报文段,并假定在时刻1接收到它的ACK,此时cwnd增加为2。接着发送了2个报文段,并假定在时刻2接收到它们的ACK,于是cwnd增加为4(对每个ACK增加1次)。这种指数增加算法一直进行到在时刻3和4之间收到8个ACK后cwnd等于ssthresh时才停止,从该时刻起,cwnd以线性方式增加,在每个往返时间内最多增加1个报文段。

正如我们在这个图中看到的那样,术语“慢启动”并不完全正确。它只是采用了比引起拥塞更慢些的分组传输速率,但在慢启动期间进入网络的分组数增加的速率仍然是在增加的。只有在达到ssthresh拥塞避免算法起作用时,这种增加的速率才会慢下来。

21.7 快速重传与快速恢复算法

拥塞避免算法的修改建议1990年提出[Jacobson 1990b]。在我们的例子(见21.5节)中已经可以看到这些实施中的修改。

第21章 TCP的超时与重传237

考虑到cwnd实际上以字节而非以报文段来维护,因此这就是我们前面提到的增加1/cwnd。在这个例子中我们计算

为885字节(使用整数算法)。当下一个ACK 1025到达时,我们计算

为991字节(在这些表达式中包括了不正确的256/8项来匹配实现计算的数值,正如我们在前面标注的那样)。

图21-9 拥塞避免的例子

这个cwnd持续增加一直到在图21-6所示的发生在10秒左右的第1次重传。图21-10是使用与图21-6相同数据得到的图表,并给出了cwnd增加的数值。

本图中cwnd的前6个值就是我们为图21-9所计算的数值。在这个图中,要想直观分辨出在慢启动过程中的指数增加和在拥塞避免过程中的线性增加之间的区别是不可能的,因为慢启动的过程太快。

第21章 TCP的超时与重传239

图21-10 当数据被发送时的发送序号和cwnd的取值

图21-11 拥塞避免的例子

cwnd的值一直持续增加,从图21-9中对应于报文段12的最终取值(1089)到图21-11中对应于报文段58的第一个取值(2426),而ssthresh的值则保持不变(512),这是因为在此过程中没有出现过重传。

当最初的2个重复的ACK(报文段60和61)到达时它们被计数,而cwnd保持不变(也就是图21-10中处理重传之前的平坦的一段)。然而,当第3个重复的ACK到达时,ssthresh被置为cwnd的一半(四舍五入到报文段大小的下一个倍数),而cwnd被置为ssthresh加上所收到的重复的ACK数乘以报文段大小(也即1024加上3倍的256),然后发送重传数据。

240TCP/IP详解,卷1:协议

图21-12显示了在路由器bsdi上截获的tcpdump的相应输出(去掉了连接建立和所有的窗口通告)。我们连接到在主机aix上的回显服务器并键入“test line”(第1行),它被回显(第2行)且回显被确认(第3行),接着我们断开了SLIP链路。

我们键入“another line”(第3行之后)并希望看到TCP超时和重传报文。的确,这一行在收到应答前被发送了6次。第4~13行显示了第1次传输和接着的4次重传,每个都产生了一个来自路由器sun的ICMP主机不可达。这正是我们所希望的:从slip来的IP数据报发往路由器bsdi(这是一个指向sun的默认路由器),并到达检测到链路中断的sun

在发生这些重传时,SLIP链路又被连通,在第14行的重传被交付。第15行是来自aix的回显,而第16行是对这个回显的确认。

这表明TCP忽略ICMP主机不可达的差错并坚持重传。我们也可以观察到所预期的在每一次重传超时中的指数退避:第1次约为2.5秒,接着乘2(约5秒),乘4(约10秒),乘8(约20秒),乘14(约40秒)。

接着我们键入输入的第3行(“line number 3”)并看到它在第17行被发送,在第18行回显,并在第19行对回显进行确认。

242TCP/IP详解,卷1:协议

图21-12 TCP对接收到的ICMP主机不可达差错的处理

现在我们希望观察在接收到ICMP主机不可达后,TCP重传并放弃的情况。于是再次断开SLIP链路,之后键入“the last line”,并观察到在TCP放弃之前该行被发送了13次(我们已经从结果中删除了第30~43行,它们是额外的重传)。

然而,我们所观察到的现象是sock程序在最终放弃时打印出来的差错信息:“没有到达主机的路由”。这与Unix的ICMP主机不可达的差错类似(图6-12)。这表明TCP保存了它在连接上收到的ICMP差错,并在最终放弃时打印出该差错,而不是“连接超时”。

最后,注意到第22~46行与第6~14行不同的重传间隔。看起来我们键入的第3行在第17~19行被发送和确认时(无任何重传),TCP更新了它的估计器。最初的重传超时时间现在是3秒,后续取值为6,12,24,48,直至上限64。

第21章 TCP的超时与重传243

图21-13显示了tcpdump的输出(去掉了连接建立、连接终止以及所有的窗口通告)。

图21-13 TCP对数据的重新分组

第1行和第2行显示了头一行(“hello there”)被发送及其ACK。接着我们拔掉以太网电缆并键入“line number 2”(14字节,包括换行)。这些数据在第3行被发送,并在第4和第5行被重传。

在第6行重传前,我们键入“and 3”(6个字节,包括换行),并观察到这个重传包括20个字节:键入的两行。当ACK在第9行到达时,它确认了这20字节的数据。

21.12 小结

本章提供了对TCP超时和重传机制的详细研究。使用的第1个例子是一个丢失的建立连接的SYN,并观察了在随后的重传和超时中怎样使用指数退避方式。

TCP计算往返时间并使用这些测量结果来维护一个被平滑的RT T估计器和被平滑的均值偏差估计器。这两个估计器用来计算下一个重传时间。许多实现对每个窗口仅测量一次RT T。Karn算法在分组丢失时可以不测量RT T就能解决重传的二义性问题。

244TCP/IP详解,卷1:协议

详细例子包括3个丢失的分组,使我们看到TCP的许多实际算法:慢启动、拥塞避免、快速重传和快速恢复。我们也能够使用拥塞窗口和慢启动门限来手工计算TCP RTT估计器,并将这些值与跟踪输出的实际数据进行比较。

以多种ICMP差错对TCP连接的影响以及TCP怎样允许对数据进行重新分组来结束本章。我们观察到“软”的ICMP差错没有引起TCP连接终止,但这些差错被保存以便在连接非正常中止时能够报告这些软差错。

习题

  1. 在图21-5中第1个超时时间计算为6秒而第2个为12秒。如果初始SYN的确认在12秒超时溢出时还没有到达,则下一次超时在什么时候发生?
  2. 在图21-5后面的讨论中,我们提到计算的超时间隔分别为图4-5中表示的6、24和48秒。但是如果观察一个从SVR4系统到一个不存在的主机的连接,则超时间隔分别为6,12,24和48秒。请问发生了什么情况?
  3. 按下面的描述比较TCP滑动窗口协议与TFTP的停止等待协议的性能。在本章中,我们在35秒(图21-6)内传输32768字节的数据,其中链路的平均RT T是1.5秒(图21-4)。计算在同样条件下TFTP需要多长时间?
  4. 在第21.7节,我们提到过收到一个重复的ACK是因为一个报文段丢失或重新进行排序。在21.5节我们看到1个丢失的报文段产生一些重复的ACK。请画图表示重新排序也会产生一些重复的ACK。
  5. 在图21-6中的时刻28.8和29.8之间有一个显而易见的点,请问这是不是一个重传?
  6. 在21.6节我们提到过,如果目的地址位于一个不同的网络上,4.3BSD Tahoe版本只执行慢启动。你认为在这里“不同的网络”是由什么决定的?(提示:参看附录 E)。
  7. 在20.2节我们提到过,在正常情况下,TCP每隔一个报文段进行一次确认,但是在图212中,我们看到接收方对每个报文段都进行了确认,请解释其中的原因?
  8. 如果默认路由占优势,那么每路由(per-route)的度量是否真的有用?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: