TCP輻輳制御(こうそうせいぎょ)とは

安定しているはずの回線で速度が上下するのは、TCPが裏でネットワークの様子をうかがいながら送信量を調整しているからです。

輻輳制御が必要な理由

インターネットは多数の接続が同じルーターや回線を共有する仕組みです。すべての送信者が最大速度でデータを送り続けると、途中のルーターのバッファがあふれてパケットロスが急増し、再送がさらに増えて全体のスループットが崩壊する「輻輳崩壊」につながりかねません。輻輳制御は、そうなる前に送信速度をネットワークの状態に合わせて自ら調整するTCPの仕組みです。

輻輳ウィンドウ(cwnd)とは

輻輳ウィンドウは、確認応答(ACK)を待たずに送信できるデータ量を表す値です。値が大きいほど一度に多くのデータを送れてスループットが上がりますが、大きすぎるとネットワークが処理しきれずパケットロスが発生します。ほとんどの輻輳制御アルゴリズムは、結局のところこの値を状況に応じて増減させるルールにすぎません。

スロースタート:少しずつ速度を探る

接続が始まったばかりの段階ではネットワークの状態が分からないため、TCPは小さなウィンドウから始め、確認応答が届くたびにウィンドウを倍々に増やしていきます。この指数関数的な増加によって適正な速度をすばやく見つけますが、パケットロスが発生するか、あらかじめ決めたしきい値に達するとスロースタートを終え、より慎重に増やす輻輳回避フェーズに移ります。

輻輳回避:損失が起きたら速度を落とす

従来型のTCPは、パケットロスを「ネットワークが混雑しているサイン」として扱います。損失を検知するとウィンドウを大きく(多くの場合半分に)減らし、その後は損失が起きない限りゆっくりと直線的に増やしていきます。この増減を繰り返すことで、スループットのグラフには特徴的なのこぎり歯状のパターンが現れます。

RenoとNew Reno:損失ベースの古典的アルゴリズム

1990年代に登場したTCP Renoは、スロースタート、輻輳回避、高速再送・高速回復を組み合わせ、損失が起きたらすぐにウィンドウを減らして素早く回復するよう設計されています。New Renoは、1つのウィンドウ内で複数のパケットが失われた場合の回復性能を改善したものです。ただし帯域幅が非常に大きい高速回線では、損失後の回復が遅く、回線を十分に使い切れないという弱点があります。

Cubic:Linuxの標準、高帯域幅向けの改良版

現在のLinuxカーネルの標準アルゴリズムであるCubicは、ウィンドウサイズを時間の3次関数(キュービック)曲線に沿って調整します。損失の直後は素早く以前の水準近くまで回復し、その後は緩やかに増加させ、前回損失が起きた地点付近では再び慎重になります。この方式は、帯域幅と遅延が大きい高速・長距離回線でReno系よりもはるかに効率的に帯域を活用できます。

BBR:損失ではなく帯域幅と遅延を直接測定する新方式

RenoやCubicのような損失ベースのアルゴリズムは「損失が起きて初めて」混雑を判断しますが、これはバッファがすでに満杯になった後にしか反応できないという弱点があります(バッファブロート問題とも関係します)。Googleが開発したBBRは、代わりにボトルネック帯域幅と直近の最小往復遅延時間をリアルタイムで推定し続け、損失が発生する前に最適な送信速度を維持しようとします。Google自身の多くのサービスで採用されており、Linuxカーネルでも選択的に利用できます。

成り立ち:1980年代の輻輳崩壊とVan Jacobsonの改良

1980年代半ばの初期のインターネットでは、送信側が失われたデータを再送し続けることでかえって混雑が悪化し、一部の回線でスループットが大幅に低下する「輻輳崩壊」が起きました。1988年にVan Jacobsonが発表した論文「Congestion Avoidance and Control」で、スロースタートと輻輳回避の考え方が示され、これが現在のTCPの動作の基礎になっています。RFC 5681などの規格に、標準的な形で記載されています。

スロースタート閾値(ssthresh)

ssthreshは、倍々の増加をやめて直線的な増加に切り替えるウィンドウサイズの境目です。損失が起きると、そのときのウィンドウの約半分に設定されるのが一般的で、安全と思われる水準までは素早く回復し、そこから先は慎重に増やしていきます。この経路で問題が起きた場所についてのTCPの「記憶」のようなものと考えると分かりやすいでしょう。

高速再送と高速回復

受信側は、順番どおりでないセグメントを受け取ると、最後に順番どおり届いたバイトの確認応答を繰り返し返します。同じ確認応答が3回重複して届くと、1つのセグメントが失われ、その後のものは届いていると判断されます。送信側はタイマーの満了を待たずにすぐ再送し、ウィンドウを最小値まで落とさず半分に減らすだけにとどめるため、タイムアウトに比べてスループットの低下がはるかに小さく済みます。

帯域幅遅延積:回線にどれだけデータが入るか

回線を使い切るために必要な送信中のデータ量は、回線の帯域幅に往復遅延時間を掛けた値になります。例えば、100Mbit/sで往復遅延が50ミリ秒の経路では、約625,000バイトが送信中になる計算です(1秒あたり1億ビットを8で割り、0.05秒を掛けた値)。ウィンドウがこれより小さいと、送信側は確認応答を待って遊んでしまうため、遠距離の高速回線では大きなウィンドウが必要で、古いアルゴリズムが苦手とする理由もここにあります。

初期ウィンドウ:最初に何パケット送るか

接続を始めたばかりのTCPは、ネットワークの情報を持たないため、少数のセグメントから始めます。2013年に発行されたRFC 6928は、初期ウィンドウを最大10セグメントまで認めており、小さなWebページやAPIの応答を1〜2往復で完了できるようにします。ただし、多くの送信者が同時に10パケットを送ると、小さなバッファでは混雑の原因になるという面もあります。

バッファブロートとアクティブキュー管理

非常に大きなバッファを持つルーターやモデムは、パケットを捨てずに溜め込むため、損失ベースのアルゴリズムはキューが長くなってからでないと異変に気づけず、回線を共有する全員の遅延が大きくなります。CoDelやfq_codelなどのアクティブキュー管理は、早めにパケットを破棄または印を付けることで、回線が混んでいても遅延を低く保てます。誰かが大きなファイルをアップロードしている間にビデオ通話が途切れる場合、バッファブロートが原因である可能性があります。

ECN(明示的輻輳通知)

ECNに対応したルーターは、輻輳を知らせるためにパケットを捨てる代わりに印を付け、受信側がそれを送信側に返します。送信側は損失があったときと同じように速度を落としますが、データを再送する必要はありません。両端とその途中のルーターが対応している必要があるため、効果の出方はネットワークによって大きく異なります。

無線LANやモバイル回線で損失ベースが苦手な理由

無線では、混雑ではなく電波干渉や弱い電波でパケットが失われることがあります。損失ベースのアルゴリズムはそれを混雑と判断してウィンドウを縮めるため、回線の余力を使い切れないことがあります。無線側には独自の再送や誤り訂正の仕組みがあり、多くの損失は隠されますが、完全ではありません。BBRのように損失だけを混雑の合図としないアルゴリズムは、こうした環境で異なる振る舞いをします。

公平性:回線を分け合うために

同じボトルネックを共有する接続同士は、おおむね均等に帯域を分け合うのが理想です。往復遅延が近いRenoの接続同士は公平な状態に近づきますが、遅延が短い接続のほうが速く成長し、より多くの帯域を得る傾向があります。異なるアルゴリズムが同居する場合も、CubicとBBRの混在で、バッファの大きさによって一方が取り分を超えて使うことがあると報告されており、大規模な事業者が慎重に検証する理由になっています。

輻輳制御はTCPの信頼性メカニズムの一部にすぎません

輻輳制御に加えて、TCPはシーケンス番号、確認応答、再送タイマーによってデータが欠けることなく順序どおりに届くことを保証しています。輻輳制御が特に扱うのは、ネットワーク全体の公平性と安定性です。これがなければ、一部の強気な送信者が回線を独占し、他の利用者の通信を悪化させてしまいます。

使っているアルゴリズムは確認できても、変更できる場面は限られます

一般消費者向けのOSやブラウザの多くは、輻輳制御アルゴリズムを切り替える設定を公開していません。この設定はクライアント側よりもサーバー側でずっと重要になるためです。Linuxサーバーでは、カーネルのパラメータでCubicやBBRなど利用可能なアルゴリズムを確認・切り替えられることが多く、だからこそアクセスの多いWebサイトや動画配信、CDNの運用者にとって選択が重要になります。

Linuxで現在の設定を確認する

Linuxでは、sysctl net.ipv4.tcp_congestion_controlで標準のアルゴリズムを、net.ipv4.tcp_available_congestion_controlで読み込まれているアルゴリズムの一覧を確認できます。ssコマンドに-iオプションを付けると、接続ごとの現在の輻輳ウィンドウ、往復遅延、再送回数などが表示され、転送が遅いときの原因調査に役立ちます。標準のアルゴリズムを変更するには通常は管理者権限が必要で、システム全体に影響するため、重要でない環境でまず試し、実際に提供するトラフィックで測定してください。

運用者はどのアルゴリズムを選ぶべきか

Cubicは長年Linuxカーネルの標準であり、幅広い条件で安定して動作するため、基準として妥当です。BBRは、大陸間通信のような長距離・損失の多い・遅延の大きい経路で効果を発揮することがありますが、結果はバッファの大きさや同じボトルネックを共有する他のトラフィックに左右され、2016年の最初のリリース以降も改訂されています。一般的なベンチマークではなく、実際のトラフィックでA/Bテストを行い、スループット、遅延、再送率を比較するのが確実です。

QUICと、アプリケーション側への輻輳制御の移行

2021年にRFC 9000として標準化されたQUICは、UDPの上で動作し、損失回復と輻輳制御をOSではなくアプリケーションのライブラリ内で実装します。これにより、ブラウザやサーバーはカーネルの更新を待たずにアルゴリズムを更新できます。仕様ではNew Renoに似た標準的な制御方式が示されており、実装によってはCubicやBBRも選べます。HTTP/3の通信でも、このページで説明した原則がそのまま当てはまります。

よくある質問

自分のデバイスが使う輻輳制御アルゴリズムを切り替えられますか?

Linuxではカーネルの設定でCubicやBBRなどを選択できます。一方WindowsやmacOS、多くのモバイルOSはこの設定を一般ユーザーに公開しておらず、実際には接続のサーバー側の方が体感速度に与える影響が大きいのが実情です。

UDPで送られる動画通話やゲームの通信にも輻輳制御は関係しますか?

UDP自体には輻輳制御の仕組みがないため、行儀の悪いUDPアプリケーションはネットワークが混雑していてもデータを送り続け、同じ回線を使う他の通信に負担をかけることがあります。QUICのようなUDPの上に構築された最新のプロトコルは、自前の輻輳制御ロジックを実装してより責任ある挙動を実現しています。

パケットロスは必ず混雑のサインですか?

いいえ。損失は混雑が原因のこともありますが、無線の干渉やケーブルの不良、機器の過負荷が原因のこともあります。従来のTCPは損失を混雑と見なすため、品質の悪い無線回線で不要に速度を落とすことがあります。

ダウンロードが最初は遅く、だんだん速くなるのはなぜですか?

スロースタートが働いているためです。接続は小さなウィンドウから素早く成長しますが、小さなファイルは最高速度に達する前に転送が終わってしまいます。短い接続を何本も使うよりも、長い接続を再利用するほうが効率的な場合が多いのはこのためで、HTTP/2などで接続の再利用が重視される理由の一つです。

回線の契約を速いプランにすれば、1本のダウンロードも速くなりますか?

必ずしもそうとは限りません。1本の接続は、契約帯域よりも往復遅延、ウィンドウサイズ、経路上の損失によって制限されることがあります。遠いサーバーからのダウンロードでは大きなプランを使い切れないことがあり、近くのサーバーやCDNなら使い切れる可能性があります。

スループットのグラフののこぎり歯は何を意味しますか?

緩やかな増加と、損失後の急な減少が繰り返された結果です。もっとなめらかな線が出る場合は、遅延や測定した帯域幅に基づいて調整する別のアルゴリズムか、余裕のある経路である可能性があります。