合同会社小村ソフト
第 5 章

輻輳制御 — TCP はネットワークの混雑に譲る

経路の混み具合は誰も教えてくれない。TCP は cwnd という自前の窓を倍々で広げ、パケットロスを合図に譲る — 探りながら送る仕組みを手計算で追う。

相手は元気でも、道が混んでいる

第 4 章のフロー制御で、相手を溺れさせることはなくなりました。では、rwnd の許す限り全力で送ってよいでしょうか。答えは否です。送信側と受信側の間には、他人の通信も流れ込む共有のネットワークがあります。途中のルータの処理能力を超えた分はキューに溜まり、キューがあふれれば捨てられます。全員が全力で送れば、再送が再送を呼び、ネットワーク全体が詰まって誰のデータも流れなくなります(輻輳崩壊 — 1980 年代に実際に起きた事故です)。

厄介なのは、経路の空き容量は誰も教えてくれないことです。rwnd のような申告はありません。そこで TCP は約束その 3 の後半として、送信側が自力で経路の容量を推定する輻輳制御を持ちます。推定値はもう 1 つの窓 — 輻輳ウィンドウ(cwnd) — として持ち、実際に送ってよい量は min(rwnd, cwnd) で決まります。相手の都合と道の都合、厳しい方に合わせるのです。

cwnd が時間とともにスロースタートで指数的に増え、しきい値から先は 1 ずつの直線的な増加に変わり、ロスの検知で半分または 1 に落ちてまた登り直すグラフ風の図

cwnd の一生。倍々で駆け上がり(スロースタート)、しきい値から先は一歩ずつ(輻輳回避)、ロスで譲って登り直す。ギザギザの鋸歯は TCP が健全に限界を探り続けている姿です。

探り方 — 倍々で駆けて、一歩ずつ詰める

容量が分からない道では、少なく始めて様子を見ながら増やすしかありません。TCP の探り方は 2 段変速です。

スロースタート — 毎 RTT で倍々
cwnd = 1 MSS(セグメント 1 個分)から始め、ACK が無事返るたびに増やす。結果として 1 → 2 → 4 → 8 → 16… と 1 RTT ごとに倍になる。名前に反して指数的な急加速
輻輳回避 — 毎 RTT で +1
しきい値(ssthresh: 前回ロスが起きた水準の記憶)を超えたら足し算に切り替え、1 RTT に 1 MSS ずつ。限界の近くをそっと探る

そして経路の限界を越えるとパケットロスが起き、第 3 章の検知が働きます。ここで証拠の重さに応じて罰を変えるのが TCP の知恵です。

重複 ACK 3 回(軽症)
後続は届いている = 道は生きている。cwnd をおよそ半分に落とし、そのまま輻輳回避で続行
タイムアウト(重症)
何も返らない = 深刻な詰まり。cwnd = 1 MSS へ戻し、スロースタートからやり直し

増やして、ぶつかって、譲って、また増やす。cwnd のグラフが描く鋸歯(のこぎり)状の波は、TCP がネットワークの容量を全員で分け合うために払い続けている挨拶のようなものです。誰かが譲らない設計だったら、インターネットは成り立っていません。

小問 5-1 — 2 つのブレーキを区別する

「誰の都合に合わせているか」で 2 つの窓を言い分けられるようにします。

Q1. フロー制御と輻輳制御の違いの説明として正しいものはどれですか。

Q2. TCP がパケットロスを「混雑の合図」として使うのはなぜですか。

Q3. 送信側がある瞬間に送ってよいデータ量(in-flight の上限)を決めるものはどれですか。

実務でどう見えるか

輻輳制御は普段は透明ですが、性能問題では必ず顔を出します。

  • 接続直後は誰でも遅い — どんな太い回線でも cwnd = 1 MSS 近辺から探り始めるため、短い通信は一度もトップスピードに達しません。小さなリクエストを大量に張り直すより、1 本のコネクションを使い回す(keep-alive)方が速い理由がこれです。
  • ロス 1 回のコストは「再送 1 個」では済まない — cwnd が半分(または 1)に落ちるので、スループットの回復に何 RTT もかかります。ロス率 1% は「1% 遅くなる」ではなく、桁違いに遅くなることがあります。
  • 速度の頭打ちには 3 つの容疑者 — rwnd(相手のバッファ)、cwnd(経路の混雑)、そして帯域そのもの。第 4 章の式に当てはめ、どの窓が min を取っているかを考えるのが切り分けの型です。

小問 5-2 — cwnd の成長を手計算する

cwnd はセグメント数(MSS 単位)で数えます。倍々と足し算、2 つの成長を計算してください。

Q4. スロースタートで cwnd = 1 MSS から送信を始めました。1 RTT ごとに cwnd が 2 倍になるとき、3 RTT 経過後の cwnd は何 MSS ですか。

MSS

Q5. しきい値(ssthresh)を超えて輻輳回避モードに入り、cwnd = 10 MSS から 1 RTT ごとに 1 MSS ずつ増やしています。5 RTT 経過後の cwnd は何 MSS ですか。

MSS

Q6. ロスを検知したときの TCP の反応は、検知のしかたで異なります。正しい組み合わせはどれですか。(第 3 章の 2 つの検知方法を思い出してください)

この章で持ち帰ること

  • フロー制御は相手の都合、輻輳制御はネットワークの都合。送ってよい量は min(rwnd, cwnd)
  • 経路の容量は申告されないので、パケットロスを混雑の合図として自力で探る
  • 成長は 2 段変速 — スロースタートは毎 RTT で倍々、輻輳回避は毎 RTT で +1 MSS
  • 罰も 2 段階 — 重複 ACK 3 回で半分、タイムアウトで 1 に戻してやり直し。鋸歯の波は健全な譲り合いの姿

これで 3 つの約束がすべて揃いました。次章は視点をアプリ側に移します。TCP が完璧に約束を守っているのに、アプリのデータが「欠ける・くっつく」ように見える — 実務で最も多い TCP の誤解、バイトストリームとフレーミングの話です。