TCP拥塞控制-图中的快速恢复

21
我一直在阅读《计算机网络:自顶向下方法》这本书,遇到了一个问题我似乎不太理解。阅读时,TCP拥塞控制有三种状态:慢启动、拥塞避免和快速恢复。我很好地理解了慢启动和拥塞避免,但快速恢复状态却很模糊。该书声称TCP的行为如下: (cwnd = 拥塞窗口大小) enter image description here
让我们来看下面的图表: 传输轮回-拥塞窗口大小图形

正如我们所看到的,在第16轮发送者发送了42个数据段,并且由于拥塞窗口大小减半(+3),我们可以推断出已经发生了3个冗余ACK。这个问题的答案声称第16至22轮处于拥塞避免状态。但为什么不是快速恢复呢?我的意思是,在出现三个重复的ACK之后,TCP进入了快速恢复,因此自那以后的每个重复的ACK都应该增加拥塞窗口。为什么图表没有这样的表示呢?我能想到的唯一合理的解释是,在这个图表中,只有三个重复的ACK,并且自那以后收到的ACK并不重复。

即使是这种情况,如果有超过3个重复的ACK会导致图表变成什么样子呢?

上述图表中是否有快速恢复状态的表示? 为什么/为什么不?

** 我已经苦苦思考了很长时间这个问题。我将非常感谢任何回复,谢谢!

更新:这里是图像。我认为一个轮回是当所有窗口中的段都被确认时。在照片中,一个回合用圆圈表示。 enter image description here 为什么在快速恢复状态下cwnd会呈指数增长?(在图像中我错误地写成了“expediently”而不是“exponentially”)

2个回答

22
更新: 我最初的答案同意解决方案,但经过仔细思考,我认为解决方案是错误的。这个答案是从头重新编写的,请仔细阅读。我展示了为什么在时间T=16进入快速恢复,并且为什么协议保留在那里直到T=22。图表中的数据支持我的理论,所以我非常确定解决方案是错误的。
让我们先澄清一些事情:慢启动呈指数增长;拥塞避免呈线性增长,而快速恢复尽管使用与慢启动相同的公式来更新值,但也呈线性增长。
让我来解释一下。 为什么我们说慢启动呈指数增长? 请注意,每收到一个ACK就会增加字节。
让我们看一个例子。假设初始化为1 MSS(MSS的值通常为1460字节,因此实际上初始化为1460)。此时,由于拥塞窗口大小只能容纳1个数据包,TCP将不发送新数据,直到该数据包得到确认。假设ACK未丢失,则这意味着大约每个RTT秒传输一个新数据包(回想一下RTT是往返时间),因为我们需要(1/2)* RTT来发送数据包,以及(1/2)* RTT来接收ACK。
因此,这导致发送速率大约为MSS / RTT bps。现在,请记住,对于每个,都会增加。因此,一旦第一个到达,变为<2*MSS>,因此现在我们可以发送2个数据包。当这两个数据包被确认时,我们将增加两次,因此现在为<4*MSS>。太好了!我们可以发送4个数据包。这些4个数据包被确认,所以我们要增加 4次!因此我们有。然后我们得到。我们实际上是每个RTT秒使翻倍(这也解释了为什么在拥塞避免中导致线性增长)。
是的,这很棘手,公式很容易让我们相信它是线性的 - 这是一个常见的误解,因为人们经常忘记这适用于每个确认的数据包。
请注意,在现实世界中,传输4个数据包并不一定会生成4个ACK。可能只生成1个ACK,但由于TCP使用累计确认,该单个ACK仍然确认了4个数据包。
为什么快速恢复是线性的? cwnd = cwnd + MSS公式在慢启动和拥塞避免中都适用。人们会认为这会导致两种状态都引起指数增长。然而,快速恢复将该公式应用于不同的上下文:当接收到重复的ACK时。其中的区别在于:在慢启动中,一个RTT确认了一堆段落,并且每个确认的段落都为cwnd的新值贡献了+1MSS,而在快速恢复中,重复的ACK浪费了一个RTT来确认单个段落的丢失,因此我们不是每个RTT秒更新cwnd N次(其中N是传输的段数),而是为丢失的一个段落更新cwnd>一次。所以我们“浪费”了一个往返时间只有一个段落,所以我们只将cwnd增加1。
关于拥塞避免 - 当我分析图表时,我会解释这个。
分析图表
好的,让我们逐一查看该图表中每一轮发生的情况。你的图片在某种程度上是正确的。首先让我澄清一些事情:
1.当我们说慢启动和快速恢复呈指数增长时,这意味着它在每一轮中呈指数增长,就像你在图片中展示的那样。所以,这是正确的。你正确地确定了用蓝色圆圈标出的回合:请注意,cwnd的值从一个圆圈到下一个圆圈呈指数增长- 1, 2, 4, 8, 16等。
2.你的图片似乎表明协议从慢启动进入快速恢复状态。这不是发生的事情。如果从慢启动进入快速恢复,我们将看到cwnd减半。但是,图表所显示的并不是这样:从T = 6到T = 7,cwnd的值没有减少一半。
好的,现在让我们逐一查看每一轮发生的情况。请注意,图表中的时间单位是一轮。因此,如果在时间T = X时传输N个段落,则假定在时间T = X + 1时已经确认了这些N个段落(当然,假设它们没有丢失)。
还要注意,我们可以通过观察图表来确定ssthresh的值。在T=6时,cwnd停止指数增长并开始线性增长,它的值不会降低。从慢启动到另一个状态的唯一可能转换,不涉及减小cwnd的是转换到拥塞避免状态,当拥塞窗口大小等于ssthresh时发生。我们可以看到,在cwnd为32时,这种情况发生了。因此,我们立即知道ssthresh初始化为32 MSS。该书在第276页(图3.53)上显示了一个非常相似的图表,作者得出了类似的结论: enter image description here 在正常情况下,当TCP第一次从指数增长转换为线性增长而不减小窗口大小时,它总是因为达到阈值并切换到拥塞避免。
最后,假设MSS至少为1460字节(通常为1460字节,因为以太网的MTU = 1500字节,我们需要考虑TCP + IP头的大小,它们共需40字节)。当cwnd超过ssthresh时,这很重要,因为cwnd的单位是MSS,而ssthresh以字节表示。
因此,我们有:
T = 1: cwnd = 1 MSS;ssthresh = 32 kB
传输1个段
T = 2:
1个段被确认 cwnd += 1;ssthresh = 32 kB cwnd的新值:2
传输2个段
T = 3:
2个段被确认 cwnd += 2;ssthresh = 32 kB cwnd的新值:4
传输4个段
T = 4:
4个段被确认 cwnd += 4;ssthresh = 32 kB cwnd的新值:8
传输8个段
T = 5:
8个段被确认 cwnd += 8;ssthresh = 32 kB cwnd的新值:16
传输16个段
T = 6:
16个段被确认 cwnd += 16;ssthresh = 32 kB cwnd的新值:32

发送32个数据段

好的,现在让我们看看会发生什么。 cwnd 达到了 ssthresh(32*1460 = 46720字节,大于32000)。现在是切换到拥塞避免的时候了。请注意,每一轮中 cwnd 的值呈指数增长,因为每个确认的数据包都会对新的 cwnd 值贡献1 MSS,而每个发送的数据包都会在下一轮中得到确认。

切换到拥塞避免模式

现在,cwnd 不会再呈指数增长,因为每个 ACK 不再贡献1 MSS。相反,每个 ACK 贡献的是 MSS*(MSS/cwnd)。例如,如果 MSS 是1460字节,cwnd 是14600字节(因此在每一轮中我们发送10个数据段),那么每个 ACK (假设每个数据段都有一个 ACK)将使 cwnd 增加 1/10 MSS(146字节)。因为我们发送了10个数据段,并且在每轮结束时我们假设每个数据段都得到了确认,所以在每轮结束时,我们将 cwnd 增加了 10 * 1/10 = 1。换句话说,每个数据段只贡献一小部分的 cwnd,因此我们每轮只会将 cwnd 增加1 MSS,而不是增加传输/确认的数据段数量。

我们将保持拥塞避免模式,直到检测到某些丢失(无论是3个重复的 ACK 还是超时)。

现在,让时钟继续...

T = 7

已确认32个数据段

cwnd += 1; ssthresh = 32 kB

新的 cwnd 值:33

发送33个数据段

请注意,尽管已确认了32个数据段,但 cwnd 从32变为33(因此每个 ACK 贡献1/32)。如果像T=6那样处于慢启动状态,我们将会有 cwnd += 32。这个新的 cwnd 值也与时间T=7时图表中看到的值相符。

T = 8

已确认33个数据段

cwnd += 1; ssthresh = 32 kB

新的 cwnd 值:34

发送34个数据段

T = 9

已确认34个数据段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:35

传输35个分段

注意,这与图表一致:在T = 9时,我们有cwnd = 35。这一直持续到T = 16...

T = 10

确认35个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:36

传输36个分段

T = 11

确认36个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:37

传输37个分段

T = 12

确认37个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:38

传输38个分段

T = 13

确认38个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:39

传输39个分段

T = 14

确认39个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:40

传输40个分段

T = 15

确认40个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:41

传输41个分段

T = 16

确认41个分段

cwnd += 1; ssthresh = 32 kB

cwnd的新值为:42

传输42个分段

暂停

现在会发生什么?图表显示,拥塞窗口大小减小到大约一半大小,然后再次线性增长。唯一可能的情况是出现了3个重复的ACK,并且协议切换到快速恢复。图表显示它不会切换到慢启动,因为那会将cwnd降至1。因此,唯一可能的转换是快速恢复。

通过进入快速恢复,我们得到ssthresh = cwnd/2。记住cwnd的单位是MSS,而ssthresh以字节为单位,我们必须小心处理。因此,新值为ssthresh = cwnd*MSS/2 = 42*1460/2 = 30660

再次注意,这与图表相符;请注意,当cwnd
略小于30时(回想一下,对于MSS=1460,比率不完全是1:1,这就是为什么即使拥塞窗口大小略低于30,我们也会达到阈值),ssthresh将在不久的将来被触发。
切换到拥塞避免还会导致cwnd的新值为ssthresh+3MSS=21+3=24(请注意单位,这里我又将ssthresh转换成了MSS,因为我们的cwnd值是以MSS计算的)。
截至目前,我们处于拥塞避免状态,T=17,ssthresh=30660字节cwnd=24
进入T=18后,可能会发生两件事情:要么我们收到重复的ACK,要么没有。如果没有(所以是新的ACK),我们将过渡到拥塞避免。但这会将cwnd降至ssthresh的值,即21。这与图表不符——图表显示cwnd保持线性增长。此外,它不会切换到慢启动,因为那会将cwnd降至1。这意味着快速恢复已经无法使用,我们正在收到重复的ACK。这会一直持续到时间T=22: T = 18 收到重复的ACK cwnd += 1; ssthresh=30660字节 cwnd的新值:25 T = 19 收到重复的ACK cwnd += 1; ssthresh=30660字节 cwnd的新值:26 T = 20 收到重复的ACK cwnd += 1; ssthresh=30660字节 cwnd的新值:27 T = 21 收到重复的ACK cwnd += 1; ssthresh=30660字节 cwnd的新值:28 T = 22 收到重复的ACK cwnd += 1; ssthresh=30660字节 cwnd的新值:29
** 暂停 **
我们仍处于快速恢复阶段,现在突然下降到1。这表明它再次进入慢启动状态。 新的值将为29 * 1460/2 = 21170,的新值为1。这也意味着尽管我们努力重传数据段,但已超时。

T = 23

cwnd = 1; ssthresh = 21170字节

传输1个数据段

T = 24

确认1个数据段

cwnd += 1; ssthresh = 21170字节

cwnd的新值为:2

传输2个数据段

T = 25

确认2个数据段

cwnd += 2; ssthresh = 21170字节

cwnd的新值为:4

传输4个数据段

T = 26

确认4个数据段

cwnd += 4; ssthresh = 21170字节

cwnd的新值为:8

传输8个数据段

...

希望以上内容能让您更加清晰明了。


1
谢谢。您解释得非常清楚,但能否解释一下为什么快速恢复不是线性的?我的意思是,每增加一个重复ACK就将窗口值增加1,如果我要在图表上绘制它,那么它应该是y=x(其中x是超过3个重复ACK的额外数量,y是cwnd的增加量)。 - Noam Solovechick
@NoamSolovechick 很高兴能帮忙。请查看更新后的答案。 - Filipe Gonçalves
1
看看更新,我发布了一张照片,说明我为什么错误地认为快速恢复是线性的,并将其与不是线性的慢启动进行了比较。非常感谢您的时间。 - Noam Solovechick
哇,你太厉害了!非常感谢!我相信这将帮助许多学习TCP拥塞控制基本原理的人。谢谢!!! :) - Noam Solovechick
@NoamSolovechick 很高兴我能帮忙!同时感谢你提出这个好问题..像这样的好问题使得SO成为一个很棒的在线资源:) - Filipe Gonçalves
显示剩余5条评论

3
在涉及“快速恢复”的TCP Reno版本中,cwnd(拥塞窗口)图应该如下所示:

enter image description here

在“慢启动”和“拥塞避免”之间仅有一个RTT时间,这个过程称为“快速恢复”。如果像《计算机网络:自顶向下方法》书中的图一样,在T16中使用直线来表示“快速恢复”过程,则在T17中的cwnd应该是21 MSS而不是(21+3) MSS,因为当它从“快速恢复”转换到“拥塞避免”时,cwnd将降至ssthresh的值。因此,书中的图是错误的。还有,@Filipe Gonçalves 的答案也是错误的。

从发送方和接收方的时间轴追踪的角度来看,还有另一个图表可以帮助您理解“快速恢复”过程。 another example of Fast Recovery

参考:

1.http://www.ijcse.com/docs/INDJCSE17-08-03-113.pdf 2.https://www.isi.edu/nsnam/DIRECTED_RESEARCH/DR_WANIDA/DR/JavisInActionFastRecoveryFrame.html


不,你的理解是错误的,上面的答案绝对正确,你提到的链接也是正确的。在第一次恢复中会发生两件事情:case:1 如果超时发生或者 case2 默认遵循拥塞避免机制。根据给定的图表,明确超时已经发生,所以上面的答案是正确的。但是当没有给出任何信息时,你的论点肯定是正确的。 - Alok Maity
此外,您提供的链接和您所考虑的独立确认,但上面的答案考虑了累积确认。 - Alok Maity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接