コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
輻輳制御と再送
輻輳制御と再送 とは
TCP が混雑時にスループットを調整する仕組みを理解します。本レッスンでは、輻輳制御と再送 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
このレッスンで学ぶこと
TCP がネットワークの混雑を検知し、流量を自動で調整する輻輳制御の仕組みを学びます。インターネットが「全員でつぶし合わない」秘訣がここにあります。
輻輳とは
輻輳 (ふくそう) とは、ネットワークが混雑している状態のことです。ルーターのバッファが溢れたり、リンクの帯域を超えたパケットが来たりすると、パケットが捨てられはじめます。
何の制御もなければ、全員が高速でパケットを送り続けてパケットロスを大量に発生させ、再送が再送を呼ぶ「輻輳崩壊」が起きます。これを防ぐのが TCP の輻輳制御です。
基本のアイデア
TCP は「パケットロスはネットワーク混雑のサイン」とみなし、ロスを検知したら送信レートを下げます。逆にロスがなければ、徐々に送信レートを上げて帯域を有効活用しようとします。
これを実現するための変数が輻輳ウィンドウ (cwnd) です。送信者は「ACK が返るまでに何バイト送って良いか」を cwnd で管理します。
スロースタートと輻輳回避
代表的なアルゴリズム TCP Reno / CUBIC の動きは次のとおりです。
- スロースタート — 最初は cwnd=1 から始め、ACK を 1 つ受け取るたびに cwnd を 1 セグメント増やす。1 RTT で返ってくる ACK の数だけ増加するため、1 RTT ごとに cwnd がおよそ 2 倍になり、指数関数的に増える。
- 輻輳回避 — cwnd が閾値 (ssthresh) を超えたら、増加量を線形に切り替える。
- ロス検知 — 重複 ACK 3 回 (Fast Retransmit) かタイムアウトでロスを検知。
- ロス後の回復 — cwnd と ssthresh を下げ、再びスロースタートまたは輻輳回避から再開。
これによりネットワークの空き帯域を「探りながら」使い、混雑したら譲り合う動きになります。
CUBIC が現代の主流
Linux カーネルのデフォルトは CUBIC というアルゴリズムです。輻輳ウィンドウの増減を 3 次関数曲線で表現し、高帯域・高遅延ネットワークで Reno より効率が良いとされます。Google が開発した BBR も最近広まっていて、ロスではなく RTT を見て調整します。
重複 ACK と Fast Retransmit
パケットが 1 つだけ落ちた場合、受信側は「次に来るべきシーケンス番号」を待っています。後続パケットが届くたびに「同じ ACK」を返し続けます。
送信側はこの重複 ACK を 3 回受け取ると「あ、間のパケットが落ちたな」と判断し、タイムアウトを待たずに即座に再送します。これが Fast Retransmit です。
ウィンドウサイズと帯域遅延積
TCP のスループットは、ざっくり下記の式で表せます。
プレーンテキスト
Throughput ≈ WindowSize / RTTウィンドウサイズが小さいと、ACK が返ってくるまでパイプを使い切れません。これを帯域遅延積 (BDP) と呼びます。
- 1 Gbps の回線、RTT 50ms → BDP は約 6.25 MB
- ウィンドウサイズが 64 KB しかないと、50 ms あたり 64 KB しか送れず、わずか 10 Mbps 程度しか出ない
このため、現代の TCP は Window Scaling 拡張で 64KB を超える大きなウィンドウを扱えるようになっています。
ACK のタイムアウト
ACK が返ってこないとき、何ミリ秒待ってから再送するかは RTO (Retransmission Timeout) で決まります。RTO は実測した RTT の平均と分散から動的に計算されます。
タイムアウト → 再送 → cwnd 急減、というのは TCP にとって最も避けたいシナリオです。なので、3 重複 ACK で先に Fast Retransmit が走るほうが望ましい。
Bufferbloat 問題
ルーター側のバッファが大きすぎると、パケットが捨てられずに延々とキューに溜まり、RTT が膨れ上がります。これを Bufferbloat と呼びます。Wi-Fi ルーターやキャリアの機材で起こりやすく、結果としてゲームやビデオ会議のラグの原因になります。最近の家庭用ルーターは Active Queue Management (CoDel など) を入れて軽減しています。
まとめ
TCP は輻輳ウィンドウを上下させながら、ネットワークの空き帯域を探り続けます。スロースタート → 輻輳回避 → ロス時のリカバリというサイクルが、世界中で同時に動いているからこそ、共有のインターネットが破綻せずに回ります。次のレッスンでは、輻輳制御を捨てた UDP がどんな用途で輝くかを見ていきます。
よくある質問
Q. なぜパケットロスを混雑の合図とみなすのですか?
A. 有線ネットワークでの伝送エラーはほぼゼロに近いため、パケットが消えるのはルーターのキューが溢れて意図的に捨てられた場合がほとんどです。そのため TCP は「ロス = 混雑」と解釈し、cwnd を下げて送信レートを落とします。Wi-Fi など無線環境ではノイズによるロスもありますが、TCP はそれを区別できないため、帯域をフルに使えないという課題があります。
Q. CUBIC と BBR の使い分けはどう考えれば良いですか?
A. CUBIC はロスを検知して cwnd を調整するロスベースのアルゴリズムです。一方 BBR は RTT の変化とスループットを観測してボトルネック帯域を推定し、バッファを溢れさせずに帯域を使い切ろうとします。Bufferbloat が起きやすい長距離・高帯域回線や衛星通信では BBR の優位性が出やすく、クラウドプロバイダーのサーバー側設定やアプリケーション側で TCP_CONGESTION オプションを指定することで切り替えられます。
Q. Window Scaling はどうすれば有効になりますか?
A. 現代の OS(Linux、Windows、macOS)はデフォルトで Window Scaling オプション(RFC 1323)を有効にしているため、通常は意識する必要はありません。ただし古いファイアウォールや NAT 機器が Window Scaling オプションを除去してしまう場合があります。Linux では sysctl net.ipv4.tcp_window_scaling で確認でき、ss -tin コマンドで実際のウィンドウサイズの推移を観察できます。
次のレッスン
次は UDP の用途 (DNS / 動画 / ゲーム) で、TCP が混雑時にスループットを調整する仕組みを理解します を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 輻輳制御/再送 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 輻輳制御/再送 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
復習ミニクイズ
TCP の輻輳制御で、最初に送信ウィンドウが指数関数的に増えるフェーズはどれですか