【入門編】 TCP輻輳制御アルゴリズム(Slow Start, Congestion Avoidance) – ネットワーク基礎とWebセキュリティ実践ガイド

こんにちは!技術ブログの案内人を務めるネットワークセキュリティスペシャリストです。

Webサイトを閲覧したり、大きなファイルをダウンロードしたりするとき、私たちは「ボタンを押せばデータが届くのが当たり前」と思いがちですよね。ですが、その裏側にあるインターネットの世界は、無数のパケット(データの小包)が猛スピードで行き交う、大混雑の道路のようなものです。

もし、送信元のサーバーが「全力で送れるだけ送っちゃえ!」と一気にデータを流し込んだらどうなるでしょうか?
途中のルーターの処理が追いつかなくなり、通信がパンクしてパケットがバタバタと消滅してしまいます。これがいわゆる「ネットワークの輻輳(ふくそう:大渋滞のこと)」です。

今回は、TCPというプロトコルが誇る超ファインプレー、「輻輳制御(Congestion Control)」の心臓部である「スロースタート(Slow Start)」と「輻輳回避(Congestion Avoidance)」の仕組みを、郵便配達の例えを交えてどこよりも分かりやすく紐解いていきます。一歩ずつ、一緒に理解を深めていきましょう!

—

1. 現実世界で例えるなら?〜郵便配達と安全な荷物量〜

TCPの輻輳制御を理解するために、まずは身近な「郵便配達」を想像してみてください。

[送信側サーバー]                              [受信側クライアント]
      |                                              |
      | --- 荷物1個(様子見) --------------------> |
      | <--- 「無事に1個届いたよ!(ACK)」 --------- |
      |                                              |
      | --- 荷物2個(倍に増やす) ----------------> |
      | <--- 「2個とも届いたよ!(ACK)」 ----------- |
      |                                              |
      | --- 荷物4個(さらに倍!) ----------------> |
      |                     :                        |

あなたが初めて荷物を届ける地域があるとします。道の混み具合も、受取人が一度にどれだけの荷物を抱えられるかも分かりません。

  • いきなりトラック満積載(100個)で向かう → 道が工事中で大渋滞、荷物を道路に落として大惨事!
  • まずは手持ち(1個)で様子を見る → 「通れた!受取人からも受領印(ACK)をもらえたぞ」
  • 次は両手(2個)、次は小さな台車(4個)… → 「いけそうだから運ぶ量を増やそう!」

このように、「相手から無事に届いたという返事(ACK)をもらいながら、徐々に一度に送る荷物の量を調整していく賢い仕組み」こそがTCPの輻輳制御です。

—

2. 鍵を握るパラメーター「CWND(輻輳ウィンドウ)」

この荷物量をコントロールするために、送信側のコンピューターが頭の中で計算している数値があります。それが CWND(Congestion Window:輻輳ウィンドウ) です。

  • CWNDとは?
  • 「相手からの『届いたよ!』という返事を待たずに、今ネットワーク上に同時に放り出して良いデータ量」の上限値です。
  • 単位はバイトですが、わかりやすく「パケット(小包)の個数」としてイメージすると理解しやすいです(専門用語では MSS = 最大セグメントサイズ 単位で数えます)。

この CWND をどう増減させるかによって、通信のフェーズが切り替わっていきます。

—

3. フェーズ1:一気に加速する「スロースタート(Slow Start)」

通信が始まって最初に突入するのが「スロースタート(Slow Start)」フェーズです。

「スロー(遅い)」という名前がついていますが、実はこれ、全然遅くありません。むしろ「指数関数的な超スピード加速」をします!

なぜ「スロー」と呼ばれるの?

最初は CWND = 1(または初期値として 10 など)という極めて控えめで「遅い(慎重な)」値からスタートするからです。

どうやって増えていくの?

相手から「届いたよ!(ACKパケット)」が1つ返ってくるたびに、CWND を +1 します。

1. ステップ1: CWND = 1 でパケットを1個送信 → ACKが1個戻る → CWND は 2 になる
2. ステップ2: CWND = 2 なのでパケットを2個送信 → ACKが2個戻る → CWND は 4 になる
3. ステップ3: CWND = 4 なのでパケットを4個送信 → ACKが4個戻る → CWND は 8 になる

【スロースタートの増え方】
往復時間(1RTT)ごとに: 1個 ➔ 2個 ➔ 4個 ➔ 8個 ➔ 16個 ➔ 32個 ...(倍々ゲーム!)

あっという間に信じられない量のデータを送り出せるようになります。しかし、この倍々ゲームをいつまでも続けていたら、あっという間にネットワークの限界を超えてしまいますよね。

そこで登場するのが「ブレーキをかける目安ライン」です。

—

4. フェーズ2:慎重に歩む「輻輳回避(Congestion Avoidance)」

倍々ゲームで加速していくと、ある「安全基準値」に達します。
この基準値のことを ssthresh(Slow Start Threshold:スロースタート閾値) と呼びます。

CWND (データ量)
  ^
  |                                     / (輻輳回避:1往復ごとに+1個ずつ)
  |                                 /
  |                             /
  | ------------------------+ (ここが ssthresh: スロースタート閾値)
  |                      /
  |                 / (スロースタート:倍々ゲームで急上昇!)
  |             /
  |         /
  |     /
  +---------------------------------------------> 時間

CWND >= ssthresh になった瞬間の挙動

CWND がこの ssthresh の値に到達すると、「ここからは未知の領域だ。慎重にいこう!」と判断し、「輻輳回避フェーズ」へ移行します。

  • 増え方の変化:
  • スロースタート:ACKが届くたびにガンガン増える(倍々ゲーム)
  • 輻輳回避: パケットを一巡全部送り終えて、全部のACKが揃って初めて CWND を 「+1個」だけ 増やす(線形増加)

急加速をやめ、1歩ずつ足元を確かめるように通信量を増やしていくわけですね。職人技のような慎重さです。

—

5. 荷物を落としたらどうなるの?(パケットロスの検知)

慎重に増やしていっても、ネットワークの限界に達したり、別の誰かが大容量通信を始めたりすると、パケットが消滅(パケットロス)してしまいます。

TCPはこの「荷物事故」を2つの方法で検知し、即座に身を引きます。

1. タイムアウト(一定時間返事がこない):

  • 「道が完全に塞がっている!」と判断します。
  • ssthresh を現在の CWND の半分に落とし、CWND を再び 最小値(1)にリセット してスロースタートからやり直します。

2. 重複ACK(同じ受領印が3回連続で届く):

  • 「後ろの荷物は届いてるのに、途中の1個だけすっ飛ばして落ちたな?」と判断します(軽微な渋滞)。
  • CWND を半分程度に落とし、スロースタートを挟まずに即座に「輻輳回避」から再開します(これを高速再転送・高速回復と呼びます)。

このように、TCPは「限界まで攻めて、こけたらスッと身を引く」という動作をミリ秒単位で繰り返しながら、回線の性能を極限まで引き出しているのです。

—

6. 実務で触ってみよう!Linux環境での確認と調整

「仕組みはわかったけれど、実際のサーバーではどう動いているの?」と気になりますよね。
Linuxサーバーがあれば、コマンド一発でこの挙動を観察できます。

① ss コマンドでリアルタイムな CWND を見る

現在接続中のTCPセッションの内部状態を表示してみましょう。

# -t: TCP, -i: 内部情報(TCPの詳細パラメーター)を表示
ss -ti

出力例の読み解き方:

ESTAB      0      0        192.168.1.10:54321      93.184.216.34:443
     cubic wscale:7,7 rto:200 rtt:15.2/0.4 ato:40 mss:1460 rcvspace:14600
     ssthresh:16 cwnd:30
  • cwnd:30:現在、返信を待たずに30個分のパケットを送信できる状態です。
  • ssthresh:16:スロースタート閾値が16に設定されており、現在は cwnd(30) > ssthresh(16) なので「輻輳回避フェーズ」で動いていることが一目で分かります!
  • cubic:使用されている輻輳制御アルゴリズムの名前です。

② 現在の輻輳制御アルゴリズムを確認・設定する

Linuxでは、古くから使われているアルゴリズム(Reno や CUBIC)から、Googleが開発した最新の BBR まで選択できます。

# 現在利用可能なアルゴリズムを確認
sysctl net.ipv4.tcp_available_congestion_control

# 現在適用されているアルゴリズムを確認
sysctl net.ipv4.tcp_congestion_control

もし設定をチューニングしたい場合(例:bbr や cubic を指定したい場合)は、/etc/sysctl.conf に以下のように追記して反映します。

# /etc/sysctl.conf に追記する設定例

# TCPの輻輳制御アルゴリズムに cubic を指定(標準的な設定)
net.ipv4.tcp_congestion_control = cubic

# 初期スロースタート時の無駄な待機を減らす設定(必要に応じて)
net.ipv4.tcp_slow_start_after_idle = 0

設定を即座に反映させるには、以下のコマンドを実行します。

# 設定ファイルの再読み込み
sudo sysctl -p

—

まとめ

最後に、今回学んだ重要ポイントをおさらいしておきましょう!

  • スロースタート: 通信開始時は小さい値から、倍々ゲーム(指数関数的)で一気に加速する!
  • ssthresh(閾値): スロースタートの暴走を止める安全ブレーキの基準ライン。
  • 輻輳回避: ssthresh を超えたら、1往復ごとに +1 ずつ慎重にデータ量を増やしていく。
  • パケットロス時: 渋滞を察知して CWND を一気に引き下げ、ネットワークの破綻を防ぐ。

私たちが普段何気なく見ているWebサイトの裏側では、パケットたちが「もっといける?」「あ、ちょっと混んできたからゆっくり行こう!」と、健気に声を掛け合いながら走っています。

こうしたTCPの思いやり(輻輳制御)があるからこそ、世界中のトラフィックがパンクすることなく快適にインターネットを使えているのですね。

インフラやサーバーの調査をするときは、ぜひ ss -ti コマンドを叩いて、パケットたちの息遣いを感じてみてください!

コメント

タイトルとURLをコピーしました