3.1. スロースタートと輻輳回避
TCP送信側しなければならない (MUST) スロースタートと輻輳回避アルゴリズムを使用して、ネットワークに注入される未処理データの量を制御します。
状態変数
これらのアルゴリズムを実装するために、TCP接続ごとの状態に2つの変数が追加されます:
- 輻輳ウィンドウ (Congestion Window, cwnd): 確認応答(ACK)を受信する前に送信側がネットワークに送信できるデータ量に対する送信側の制限
- 受信側通知ウィンドウ (Receiver's Advertised Window, rwnd): 未処理データ量に対する受信側の制限
cwndとrwndの最小値がデータ送信を制御します。
別の状態変数であるスロースタート閾値 (Slow Start Threshold, ssthresh) は、データ送信を制御するためにスロースタートアルゴリズムと輻輳回避アルゴリズムのどちらを使用するかを決定するために使用されます。
スロースタートの目的
未知の条件のネットワークへの送信開始には、不適切に大きなデータバーストでネットワークを輻輳させることを避けるために、TCPがネットワークをゆっくりと探索して利用可能な容量を決定する必要があります。スロースタートアルゴリズムは次の目的で使用されます:
- 転送の開始時
- 再送信タイマーによって検出された損失を修復した後
スロースタートは、TCP送信側がスロースタート、輻輳回避、および損失回復アルゴリズムでネットワークにデータを解放するために使用する「ACKクロック」を開始する役割も果たします。
初期ウィンドウ (IW)
IW、つまりcwndの初期値は、上限として次のガイドラインを使用して設定しなければならない (MUST):
SMSS > 2190バイトの場合:
IW = 2 * SMSSバイトで、2セグメントを超えてはならない
(SMSS > 1095バイト) かつ (SMSS <= 2190バイト)の場合:
IW = 3 * SMSSバイトで、3セグメントを超えてはならない
SMSS <= 1095バイトの場合:
IW = 4 * SMSSバイトで、4セグメントを超えてはならない
[RFC3390]で規定されているように、SYN/ACKとSYN/ACKの確認応答は輻輳ウィンドウのサイズを増加させてはならない (MUST NOT)。さらに、SYNまたはSYN/ACKが失われた場合、正しく送信されたSYNの後に送信側が使用する初期ウィンドウは、最大SMSSバイトからなる1セグメントでなければならない (MUST)。
IW設定の詳細な根拠と議論は[RFC3390]で提供されています。
Path MTU Discoveryに関する考慮事項
1つ以上のセグメントの初期輻輳ウィンドウがPath MTU Discovery [RFC1191]とともに実装され、使用されたMSSが大きすぎることが判明した場合、より小さなセグメントの大きなバーストを避けるために、輻輳ウィンドウcwndは減少すべきである (SHOULD)。より具体的には、cwndは古いセグメントサイズと新しいセグメントサイズの比率だけ減少すべきである (SHOULD)。
初期ssthresh値
ssthreshの初期値は任意に高く設定すべきである (SHOULD) (例えば、可能な最大通知ウィンドウのサイズに設定)が、ssthreshは輻輳に応じて減少しなければならない (MUST)。
ssthreshをできるだけ高く設定することで、任意のホスト制限ではなくネットワーク条件が送信レートを決定できるようになります。
アルゴリズムの選択
cwnd < ssthreshの場合、スロースタートアルゴリズムを使用cwnd > ssthreshの場合、輻輳回避アルゴリズムを使用cwndとssthreshが等しい場合、送信側はスロースタートまたは輻輳回避のいずれかを使用できる
スロースタートアルゴリズム
スロースタート中、TCPは新しいデータを累積的に確認応答する受信したACKごとに、cwndを最大SMSSバイト増加します。スロースタートは次の場合に終了します:
- cwndがssthreshを超える(または、上記のように、オプションでそれに達する)場合、または
- 輻輳が観測された場合
推奨されるcwnd増加方法
従来、TCP実装は新しいデータをカバーするACKを受信すると正確にSMSSバイトだけcwndを増加してきましたが、次の方法でcwndを増加することを推奨します (RECOMMEND):
cwnd += min (N, SMSS) (式 2)
ここで、Nは受信したACKで確認応答された以前に未確認応答のバイト数です。
この調整は適切バイト計数(Appropriate Byte Counting)[RFC3465]の一部であり、「ACK分割」[SCWA99]と呼ばれるメカニズムを使用して送信側にcwndを人為的に膨張させようとする不正な受信側に対する堅牢性を提供します。
輻輳回避アルゴリズム
輻輳回避中、cwndは1ラウンドトリップ時間(RTT)ごとに約1フルサイズセグメント増加します。輻輳回避は輻輳が検出されるまで続きます。
cwnd増加のガイドライン
輻輳回避中のcwnd増加の基本ガイドラインは:
- cwndをSMSSバイト増加してもよい (MAY)
- RTTごとに1回、式(2)に従ってcwndを増加すべきである (SHOULD)
- cwndをSMSSバイト以上増加してはならない (MUST NOT)
推奨される実装
輻輳回避中にcwndを増加する推奨される (RECOMMENDED) 方法は、新しいデータのACKによって確認応答されたバイト数を数えることです。確認応答されたバイト数がcwndに達すると、cwndを最大SMSSバイトまで増加できます。
輻輳回避中、cwndはRTTごとにSMSSバイトを超えて増加してはならない (MUST NOT) ことに注意してください。
代替式
輻輳回避中にcwndを更新するためにTCPが使用してもよい (MAY) 別の一般的な式を式(3)に示します:
cwnd += SMSS*SMSS/cwnd (式 3)
この調整は、新しいデータを確認応答する受信ACKごとに実行されます。式(3)は、RTTごとに1フルサイズセグメントだけcwndを増加させるという基本原則の許容できる近似を提供します。(受信側が2パケットに1つを確認応答する接続では、(3)は許容されるよりも攻撃的ではありません — 約2RTTごとにcwndを増加させます。)
式(3)の実装上の注意
注意 1: TCP実装では一般に整数演算が使用されるため、輻輳ウィンドウがSMSS*SMSSより大きい場合、式(3)の式はcwndを増加させない可能性があります。上記の式が0を与える場合、結果は1バイトに丸めすべきである (SHOULD)。
注意 2: 古い実装では、式(3)の右辺に追加の加算定数があります。これは誤りであり、実際に性能低下を招く可能性があります[RFC2525]。
注意 3: いくつかの実装ではcwndをバイト単位で、他ではフルサイズセグメント単位で保持しています。後者は式(3)を使うのが困難であり、前述のカウントベースのアプローチを好むかもしれません。
損失への応答
TCP送信側が再送信タイマーを使用してセグメント損失を検出し、指定されたセグメントがまだ再送信タイマーによって再送信されていない場合、ssthreshの値は式(4)で与えられる値以下に設定しなければならない (MUST):
ssthresh = max (FlightSize / 2, 2*SMSS) (式 4)
ここで、上記のように、FlightSizeはネットワーク内の未処理データの量です。
一方、TCP送信側が再送信タイマーを使用してセグメント損失を検出し、指定されたセグメントがすでに再送信タイマーによって少なくとも1回再送信されている場合、ssthreshの値は一定に保たれます。
FlightSizeに関する実装上の注意
陥りやすい間違いは、単にcwndを使用することではなくFlightSizeを使用することであり、いくつかの実装では値がrwndを大きく超えて増加する可能性があります。
タイムアウト後のcwndの設定
さらに、タイムアウト時([RFC2988]で規定)、cwndは損失ウィンドウLW以下に設定しなければならない (MUST)。LWは1フルサイズセグメントに等しくなります(IWの値に関係なく)。
したがって、ドロップされたセグメントを再送信した後、TCP送信側はスロースタートアルゴリズムを使用して、ウィンドウを1フルサイズセグメントからssthreshの新しい値まで増加させ、その時点で輻輳回避が再び引き継ぎます。
スプリアス再送信
[FF96]および[RFC3782]に示されているように、タイムアウト後のスロースタートに基づく損失回復は、重複ACKを引き起こすスプリアス再送信を引き起こす可能性があります。これらの重複ACKの到着に対する反応は、TCP実装によって大きく異なります。この文書はそのようなACKの処理方法を規定しませんが、さらなる注意、実験、および仕様の利益になる可能性がある領域として指摘します。