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

Noa*_*ick 16 networking tcp network-protocols congestion-control

我一直在阅读"计算机网络:自上而下的方法"一书,并遇到了一个我似乎不理解的问题.

正如我所读到的,TCP拥塞控制有三种状态:慢启动,拥塞避免和快速恢复.我很了解慢启动和拥塞避免,但快速恢复非常模糊.该书声称TCP的行为方式如下:(cwnd =拥塞窗口) 在此输入图像描述
我们来看下面的图表: 传输轮 - 拥塞窗口大小图

我们可以看到,在第16轮,发送方发送了42个段,并且因为拥塞窗口大小减半(+3),我们可以推断出有3个重复ACK.这个问题的答案声称 16到22之间的轮次处于拥塞避免状态.但为什么不快速恢复?我的意思是,在三次重复的ACK之后,TCP进入快速恢复并且每隔一次重复的ACK都应该增加拥塞窗口.为什么图表没有表示?我能想到的唯一合理的解释是,在这个图中,只有三个重复的ACK,并且之后收到的ACK不是重复的.

即使是这种情况,如果有超过3个重复的ACK,图表将如何显示?**

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

**我一直在努力回答这个问题很长一段时间.我会很高兴回复,谢谢!

更新这里是图像.我认为轮次定义为窗口中的所有段都被确认.在照片中,圆圈显示为圆圈. 在此输入图像描述 为什么cwnd在快速恢复状态下呈指数级增长?(在图像中我偶然写了而不是指数地写)

Fil*_*ves 13

更新:我的原始答案与解决方案一致,但仔细考虑之后,我认为解决方案是错误的.这个答案是从头开始重写的; 请仔细阅读.我说明为什么在时间T = 16时进入快速恢复以及为什么协议保持在那里直到T = 22.图中的数据支持我的理论,所以我非常肯定解决方案是完全错误的.

让我们从直接设置开始:慢启动以指数方式增长; 拥塞避免线性增长,快速恢复线性增长,即使它使用与慢启动相同的公式来更新值cwnd.

请允许我澄清一下.

为什么我们说慢启动cwnd呈指数级增长?

注意,每收到的ACKcwnd都会增加MSS字节.

我们来看一个例子吧.假设cwnd初始化为1 MSS(MSS的值通常为1460字节,因此实际上这意味着cwnd初始化为1460).此时,由于拥塞窗口大小只能容纳1个数据包,因此在确认此数据包之前,TCP不会发送新数据.假设ACK没有丢失,这意味着每RTT秒传输大约一个新数据包(回想一下RTT是往返时间),因为我们需要(1/2)*RTT来发送数据包,并且( 1/2)*ACK用于到达ACK.

因此,这导致大致MSS/RTT bps的发送速率.现在,请记住,每个ACK,cwnd增加MSS.因此,一旦第一个ACK到达,cwnd变为2*MSS,所以现在我们可以发送2个数据包.当这两个数据包被确认时,我们增加cwnd 两次,所以现在cwnd4*MSS.大!我们可以发送4个数据包.这4个数据包被确认,所以我们增加了cwnd4倍!所以我们有cwnd = 8*MSS.然后我们得到cwnd = 16*MSS.我们基本上cwnd每个RTT秒加倍(这也解释了为什么cwnd = cwnd+MSS*(MSS/cwnd)在拥塞避免导致线性增长)

是的,这很棘手,公式cwnd = cwnd+MSS很容易让我们相信它是线性的 - 一种常见的误解,因为人们经常忘记这适用于每个已确认的数据包.

注意,在现实世界中,发送4个分组不一定产生4个ACK.它可能ACK仅生成1 ,但由于TCP使用累积ACK,因此该单个ACK仍然确认4个数据包.

为什么Fast Recovery是线性的?

cwnd = cwnd+MSS公式适用于慢启动和避免拥塞.有人会认为这导致两种状态都会引发指数增长.但是,快速恢复在不同的上下文中应用该公式:当收到重复的ACK时.其中存在差异:在慢速启动时,一个RTT确认了一大堆段,每个确认段对+ 1MSS贡献了新值cwnd,而在快速恢复中,重复的ACK正在浪费RTT以确认丢失了单段,所以我们不是cwnd每个RTT秒更新N次(其中N是传输的段数),我们正在为一个丢失的段更新cwnd 一次.所以我们只用一个段"浪费"一次往返,所以我们只增加cwnd1.

关于拥塞避免 - 我将在下面分析图表时解释这一点.

分析图表

好的,让我们看看该图中到底发生了什么.你的照片在某种程度上是正确的.我先说清楚一些事情:

  1. 当我们说慢启动和快速恢复成倍增长,这意味着它呈指数级增长一轮一轮的,正如你在图片显示.所以,这是正确的.你正确地用蓝色圆圈识别了圆形:注意值cwnd从一个圆圈到下一个圆圈呈指数增长--1,2,4,8,16 ......
  2. 您的图片似乎表示在慢启动后,协议进入快速恢复.这不是发生的事情.如果从慢启动进入快速恢复,我们会看到cwnd减半.这不是图表显示的:cwnd从T = 6到T = 7 ,值不会减少到一半.

好的,现在让我们看看每轮都会发生什么.请注意,图表中的时间单位是圆形.因此,如果在时间T = X,我们发送N个段,则假设在时间T = X + 1,这些N个段已被确认(假设它们当然没有丢失).

还要注意我们如何ssthresh通过查看图表来判断价值.在T = 6时,cwnd停止指数增长并开始线性增长,其值不会减少.从慢启动到不涉及减少的另一个状态的唯一可能的转变cwnd是到拥塞避免的过渡,这在拥塞窗口大小等于时发生ssthresh.我们可以在图中看到,当cwnd32为32 时,会发生这种情况.因此,我们立即知道它ssthresh被初始化为32 MSS.这本书在第276页(图3.53)显示了一个非常相似的图表,作者得出了类似的结论:

在此输入图像描述

在正常情况下,这就是发生的情况 - 当TCP首次从指数增长切换到线性增长而不减小窗口大小时,总是因为它达到阈值并切换到拥塞避免.

最后,假设MSS至少有1460个字节(通常是1460个字节,因为以太网有MTU = 1500字节,我们需要考虑TCP + IP头的大小,它们一起需要40个字节).这是看的时候很重要cwnd超过ssthresh,因为cwnd的单位MSSssthresh以字节为单位表示.

所以我们走了:

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在数轮之间呈指数增长,因为每个确认的数据包对新值的贡献为1 MSS cwnd,并且在下一轮中确认发送的每个数据包.

切换到拥塞避免

现在,cwnd不会成倍增加,因为每个人ACK都不会再贡献1个MSS.相反,每个人都有所ACK贡献MSS*(MSS/cwnd).因此,例如,如果MSS是1460个字节,cwnd是14600个字节(以便在每一轮的开始,我们正在发送10个段),那么每个ACK(假设一个ACK每段)将增加cwnd1/10MSS(146个字节).由于我们发送了10个分段,并且在本轮结束时我们假设每个分段都被确认,然后在回合结束时我们增加cwnd10 * 1/10 = 1.换句话说,每个段贡献一小部分,cwnd使得我们cwnd每轮增加1 MSS.所以现在每轮增加cwnd1而不是传递/确认的段数.

我们将保持拥塞避免,直到检测到一些丢失(3个重复的ACK或超时).

现在,让时钟恢复......

T = 7

承认了32个细分市场

cwnd + = 1; ssthresh = 32 kB

cwnd的新价值:33

传输33段

请注意cwnd,即使确认了32个段,每个段也会从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的单位MSSssthresh以字节为单位,我们必须要小心这一点.因此,新的价值是ssthresh = cwnd*MSS/2 = 42*1460/2 = 30660.

再次,这与图表对齐; 通知,ssthresh在不久的将来,当cwnd小于30 时会被击中(回想一下,当MSS = 1460时,该比率不完全是1:1,这就是为什么我们即使拥堵窗口大小略低于30也达到了阈值).

切换到拥塞避免也会引起新的价值的cwndssthresh+3MSS = 21+3 = 24(记得要小心单位,在这里我转换ssthresh成MSS再次因为我们的价值观cwnd在MSS计数).

截至目前,我们正在避免拥堵,T = 17,ssthresh = 30660 bytes并且cwnd = 24.

在进入T = 18时,可能会发生两件事:要么我们收到重复的ACK,要么我们没有.如果我们不这样做(所以这是一个新的ACK),我们将过渡到拥塞避免.但这会cwnd降低到ssthresh21 的值.这与图表不匹配 - 图表显示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

**暂停**

我们仍处于快速恢复状态,现在,突然cwnd降至1.这表明它再次进入缓慢启动状态.新的价值ssthresh将是29*1460/2 = 21170cwnd = 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段

...

我希望这说清楚.