「コンピュータネットワーク:トップダウンアプローチ」という本を読んでいて、理解できない質問に遭遇しました。
私が読んだように、TCP輻輳制御には、スロースタート、輻輳回避、高速復旧の3つの状態があります。スロースタートと輻輳回避はよく理解していますが、高速復旧はかなりあいまいです。この本は、TCPはこのように動作する:(cwnd =輻輳ウィンドウ)と主張しています。
次のグラフを見てみましょう。
ご覧のとおり、ラウンド16で送信者は42セグメントを送信し、輻輳ウィンドウサイズが半分(+3)になっているため、3つの重複ACKがあったと推測できます。この質問に対する答えは、16と22の間のラウンドは輻輳回避状態にあるであると主張しています。しかし、なぜ高速回復ではないのですか?つまり、3つの重複ACKの後TCPは高速リカバリに入り、他のすべての重複ACKは輻輳ウィンドウを増やすはずなので、グラフにそれが表されないのはなぜですか?私が考えることができる唯一の合理的な説明はこのグラフでは、重複したACKは3つしかなく、それ以降に受信されたACKは重複していませんでした。
その場合でも、ACKが3つ以上重複しているとしたら、グラフはどのように見えるでしょうか。 **
**私は長い間その質問に答えるのに苦労してきました。お返事をよろしくお願いします、ありがとうございます!
[〜#〜] update [〜#〜]これが画像です。ラウンドは、ウィンドウ内のすべてのセグメントがACKされているときと定義されていると思います。写真では、丸が丸で示されています。 Fast Recovery状態のときにcwndが指数関数的に成長するのはなぜですか?(画像では、指数関数的ではなく、誤って適切に書き込んだ)
[〜#〜] update [〜#〜]:私の元の答えは解決策に同意しましたが、慎重に検討した結果、解決策は間違っていると思います。この答えは最初から書き直されました。よくお読みください。時間T = 16で高速リカバリが開始される理由と、プロトコルがT = 22までそこにとどまる理由を示します。グラフのデータは私の理論を裏付けているので、私は解決策が明らかに間違っていることをかなり確信しています。
まず、何かをまっすぐに設定してみましょう。スロースタートは指数関数的に増加します。 cwnd
の値を更新するためにスロースタートと同じ式を使用している場合でも、輻輳回避は直線的に増加し、高速リカバリは直線的に増加します。
はっきりさせておきます。
スロースタートが指数関数的にcwnd
成長すると言うのはなぜですか?
cwnd
は、受信したACKごとにMSS
バイトずつ増加することに注意してください。
例を見てみましょう。 cwnd
が1MSSに初期化されているとします(MSSの値は通常1460バイトであるため、実際にはcwnd
は1460に初期化されます)。この時点で、輻輳ウィンドウサイズは1パケットしか保持できないため、TCPは、このパケットが確認されるまで新しいデータを送信しません。ACKが失われていないと仮定すると、これは約1パケットを意味します。パケットを送信するには(1/2)* RTTが必要であり、ACKが到着するには(1/2)* RTTが必要なため、新しいパケットはRTT秒ごとに転送されます(RTTはラウンドトリップ時間であることを思い出してください)。
したがって、この結果、送信レートはおおよそMSS/RTT bpsになります。ここで、ACK
ごとに、cwnd
がMSS
ずつインクリメントされることに注意してください。したがって、最初のACK
が到着すると、cwnd
は_2*MSS
_になるので、2つのパケットを送信できます。これらの2つのパケットが確認されると、cwnd
twiceが増加するため、cwnd
は_4*MSS
_になります。すごい! 4パケット送信できます。これらの4つのパケットは確認応答されるため、cwnd
を4回インクリメントします。つまり、_cwnd = 8*MSS
_があります。そして、_cwnd = 16*MSS
_を取得します。基本的に、RTT秒ごとにcwnd
を2倍にします(これにより、輻輳回避のcwnd = cwnd+MSS*(MSS/cwnd)
が線形成長につながる理由も説明されます)
はい、注意が必要です。式_cwnd = cwnd+MSS
_を使用すると、線形であると簡単に信じられます。これは、確認済みの各パケットに適用されることを忘れがちなため、よくある誤解です。
現実の世界では、4つのパケットを送信しても必ずしも4つのACKが生成されるとは限らないことに注意してください。これは1つのACK
のみを生成する可能性がありますが、TCPは累積ACKを使用するため、その単一のACK
はまだ4つのパケットを確認しています。
高速復旧が線形である理由
_cwnd = cwnd+MSS
_式は、スロースタートと輻輳回避の両方に適用されます。これにより、両方の州が指数関数的成長を引き起こすと考えられます。ただし、高速リカバリでは、重複するACKを受信した場合など、別のコンテキストでその式が適用されます。ここに違いがあります。スロースタートでは、1つのRTTが一連のセグメント全体を確認し、確認された各セグメントが+ 1MSSでcwnd
の新しい値に寄与しましたが、高速リカバリでは、重複ACKがRTTを浪費して単一のセグメントであるため、RTT秒ごとにcwnd
をN回更新する代わりに(Nは送信されたセグメントの数)、LOSTであったセグメントのcwnd
onceを更新します。したがって、1つのセグメントだけで1つのラウンドトリップを「無駄にした」ので、cwnd
を1だけインクリメントします。
輻輳回避について-これはグラフを分析するときに以下で説明します。
グラフの分析
では、そのグラフで何が起こっているかを正確に見ていきましょう。あなたの写真はある程度正しいです。最初にいくつかのことをクリアしましょう:
cwnd
の値が1つの円から次の円に指数関数的に増加することに注目してください-1、2、4、8、16、.. ..cwnd
が半分になります。これはグラフが示すものではありません。cwnd
の値はT = 6からT = 7まで半分に減少しません。では、各ラウンドで何が起こるかを正確に見てみましょう。グラフの時間単位はラウンドであることに注意してください。したがって、時間T = XでNセグメントを送信する場合、時間T = X + 1でこれらのNセグメントがACKされたと想定されます(もちろん、それらは失われなかったと仮定します)。
また、グラフを見るだけでssthresh
の値を確認する方法にも注意してください。 T = 6で、cwnd
は指数関数的に増加しなくなり、直線的に増加し始めます。その値は減少しません。スロースタートからcwnd
の減少を伴わない別の状態への唯一の可能な移行は、輻輳ウィンドウサイズがssthresh
に等しい場合に発生する輻輳回避への移行です。グラフを見ると、これはcwnd
が32のときに発生することがわかります。したがって、ssthresh
が32MSSに初期化されていることがすぐにわかります。この本は276ページ(図3.53)に非常によく似たグラフを示しており、著者は同様の結論を出している。
通常の状態では、これが発生します-TCPがウィンドウのサイズを縮小せずに指数関数的成長から線形成長に初めて切り替わるとき、それは常にしきい値に達して切り替わったためです輻輳回避に。
最後に、MSS
が少なくとも1460バイトであると仮定します(イーサネットはMTU = 1500バイトであり、TCP + IPヘッダーのサイズを考慮する必要があるため、通常は1460バイトです) 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 =
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より大きい)。混雑回避に切り替える時が来ました。確認された各パケットが1 MSSでcwnd
の新しい値に寄与し、送信されたすべてのパケットが次のラウンドで確認されるため、cwnd
の値がラウンド間で指数関数的に増加することに注意してください。
輻輳回避への切り替え
これで、各cwnd
が1つのMSSに寄与しなくなるため、ACK
は指数関数的に増加しなくなります。代わりに、各ACK
はMSS*(MSS/cwnd)
で貢献します。したがって、たとえば、MSS
が1460バイトで、cwnd
が14600バイトの場合(したがって、各ラウンドの開始時に10セグメントを送信します)、各ACK
(セグメントごとに1つのACK
を想定)はcwnd
を_1/10
_ MSSずつ増やします。 (146バイト)。 10個のセグメントを送信し、ラウンドの終了時にすべてのセグメントが確認応答されたと想定するため、ラウンドの終了時にcwnd
を_10 * 1/10 = 1
_増やしました。言い換えると、各セグメントはcwnd
にわずかな割合を与えるため、ラウンドごとにcwnd
を1MSSずつインクリメントします。そのため、各ラウンドは、転送/確認されたセグメントの数ではなく、cwnd
を1ずつインクリメントします。
何らかの損失が検出されるまで(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 = 1
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 = 1
承認された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があり、プロトコルが高速リカバリに切り替わったことです。グラフは、[〜#〜] not [〜#〜]がスロースタートに切り替わらないことを示しています。これは、cwnd
が1に下がるためです。したがって、可能な遷移は高速にすることだけです。回復。
高速リカバリを入力すると、_ssthresh = cwnd/2
_が得られます。 cwnd
の単位はMSS
であり、ssthresh
はバイト単位であることを覚えておいてください。したがって、新しい値は_ssthresh = cwnd*MSS/2 = 42*1460/2 = 30660
_です。
繰り返しますが、これはグラフと一致しています。 ssthresh
が30よりわずかに小さい場合、近い将来cwnd
がヒットすることに注意してください(MSS = 1460では、比率が正確に1:1ではないことを思い出してください。そのため、輻輳ウィンドウのサイズが30をわずかに下回っていても、しきい値に達します。 )。
輻輳回避への切り替えにより、cwnd
の新しい値も_ssthresh+3MSS = 21+3 = 24
_になります(ssthresh
の値はMSSでカウントされるため、ここではcwnd
を再度MSSに変換しました)。
現在、T = 17、_ssthresh = 30660 bytes
_および_cwnd = 24
_を使用して、輻輳を回避しています。
T = 18に入ると、2つのことが起こります。重複した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 = 2
重複した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 = 21170
_、および_cwnd = 1
_になります。また、セグメントを再送信するための努力にもかかわらず、タイムアウトが発生したことを意味します。
T = 2
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セグメントを送信
...
それが明らかになることを願っています。
TCP Reno
(Fast Recovery
を含むTCPのバージョン)では、cwnd
(輻輳ウィンドウ)グラフは次のようになります。
Slow Start
とCongestion Avoidance
の間の1つのRTT時間のみがFast Recovery
です。 「コンピュータネットワーク:トップダウンアプローチ」の本のグラフのように、T16の直線を使用して、Fast Recovery
プロセスを表します。 、次にT17のcwnd
は21になります(21 + 3)MSSの代わりのMSS、なぜならFast Recovery
からCongestion Avoidance
に移行すると、cwnd
はssthresh
の値になります。したがって、本のグラフは間違っています。また、@ FilipeGonçalvesの答えも間違っています。
送信者と受信者のタイムライントレースの観点からの別のグラフがあります。これは、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