TL

TCPのフロー制御とウィンドウスケーリング

受信側を溢れさせずに帯域を使い切る仕組みが腑に落ちる。スライディングウィンドウとスケーリングの内部を、SWSやNagleとの相互作用まで追える。

応用TCPフロー制御ウィンドウスケーリングNagleトランスポート層最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.フロー制御は受信側のバッファ空きを表す広告ウィンドウ(rwnd)で送信量を絞り、輻輳制御(cwnd)とは別物。実効の送信窓は両者の最小値。
  • 2.16ビットのウィンドウ欄は最大64KBしか表せない。高帯域・長RTT路では足りず、ウィンドウスケールオプションで左シフト量を交渉し最大1GBまで拡張する。
  • 3.Silly Window Syndromeは細切れ送信で効率を壊す病。送信側はNagle、受信側は窓更新の抑制で防ぐが、両者が噛み合うと遅延が表面化する。

フロー制御は「受信側を守る」仕組み

TCPには送信ペースを律する制御が2つあります。混同されがちですが起源も守る対象も違います。

  • フロー制御(flow control): 受信側のバッファが溢れないように送信側を絞る。エンド間の局所的な問題。
  • 輻輳制御(congestion control): 経路上のネットワークが溢れないように送信側を絞る。共有資源の問題。

本稿はフロー制御の側を扱います。輻輳制御の数理は AIMD と輻輳制御の数理 を参照してください。両者は独立に算出され、送信側が一度に流せる未確認データ量(in-flight)の上限は、受信側が許す rwnd(受信窓) と、ネットワークが許す cwnd(輻輳窓)最小値 で決まります。

実効送信ウィンドウ = min(rwnd, cwnd)

どちらか小さい方がボトルネックです。受信側がいくらバッファを広げても経路が細ければ無駄で、逆もまた然り。TCP/UDPの役割分担は TCP と UDP の違い を前提とします。

スライディングウィンドウの内部

フロー制御の実体は スライディングウィンドウ です。受信側は確認応答(ACK)のたびに「あと何バイト受け取れるか」を 広告ウィンドウ(advertised window) としてヘッダの16ビット欄に載せて返します。これが受信バッファの空き容量そのものです。

送信側はシーケンス番号空間を4区分で管理します。

 |---- 1 ----|---- 2 ----|------ 3 ------|---- 4 ----|
   送信&ACK済   送信&未ACK    送信可(窓内)   送信不可(窓外)
              ^SND.UNA     ^SND.NXT
              |<------ ウィンドウ幅 ------>|
  • 区分2(SND.UNA から SND.NXT): 送ったがACK待ちの未確認データ。
  • 区分3: まだ送っていないが窓に空きがあり送れるデータ。

ACKが届くと左端(SND.UNA)が右へ進み、窓全体が右へ「滑る」。これがスライディングの名の由来です。受信側がバッファを読み出して空きが増えれば広告ウィンドウが拡大し、アプリの読み出しが遅れて空きが尽きれば広告ウィンドウは0になります。これを ゼロウィンドウ と呼びます。

ゼロウィンドウとウィンドウプローブ

受信窓が0になると送信側は送信を止めます。問題は、空きが復活したことを知らせる「窓更新ACK」が失われると永久にデッドロックする点です。これを防ぐため送信側は ウィンドウプローブ(persist timer) で1バイトだけ試送し続け、受信側に最新の窓を再広告させます。ゼロウィンドウは異常ではなく、受信アプリの処理が詰まっているサインです。

ウィンドウスケーリング:16ビットの壁を越える

ヘッダのウィンドウ欄は16ビット、つまり最大65,535バイト(約64KB)しか表現できません。ここに高速回線時代の根本問題があります。

リンクを満たすのに必要な未確認データ量は 帯域遅延積(BDP, Bandwidth-Delay Product) で決まります。

BDP = 帯域 × RTT
例: 1Gbps × 100ms = 約12.5MB

BDPが12.5MBの経路で窓が64KBしか取れなければ、64KB送るたびにACKを待って停止し、回線の0.5%しか使えません。窓が小さいほどスループットが頭打ちになる関係は 帯域・レイテンシ・スループット の通りです。

これを解くのが ウィンドウスケールオプション(RFC 7323) です。原理は単純で、広告する窓の値に左シフト量(スケールファクタ)を掛けて解釈させます。

実際の受信窓 = ヘッダのウィンドウ値 << スケールファクタ
観点スケーリングなしウィンドウスケーリングあり
最大窓サイズ65,535バイト(約64KB)65,535 << 14 = 約1GB
有効ビット数16ビット実質30ビット(14ビットの左シフト)
交渉のタイミングなしSYN/SYN-ACKでのみ。確立後は変更不可
スケールファクタ0〜14(左シフト量、片方向ごとに独立)
適さない場面高BDP路で頭打ちなし(基本的に常時有効が望ましい)

ここで決定的に重要なのは、スケールファクタはコネクション確立時の3ウェイハンドシェイクでしか交渉できない ことです。SYNとSYN-ACKの両方にオプションが載って初めて双方向で有効になり、片方が載せなければそのコネクションは全期間スケーリング無効です。確立後の途中変更はできません。ハンドシェイクの流れは TCPの状態遷移とコネクション確立・切断の内部 を参照してください。

中間装置がスケールを壊すと窓計算が破綻する

古いファイアウォールやNATがSYNのスケールオプションを削ったり、片方向のSYNだけ通したりすると、片側がシフト前提・片側がシフトなしで窓を解釈し、実窓が桁違いにずれます。結果は「確立はするのに転送が極端に遅い/止まる」という再現性の低い障害です。スケールは片方向ごとに独立した値を持つ点も見落とされがちです。

Silly Window Syndrome:細切れが効率を壊す

スライディングウィンドウには Silly Window Syndrome(SWS、愚かな窓症候群) という固有の病があります。受信アプリが1バイトずつ読み、そのたびに「1バイト空きました」と広告すると、送信側も1バイトずつ送る。40バイト以上のヘッダ(IP+TCP)で1バイトのデータを運ぶ、極端に非効率な状態に陥ります。

原因は送受の両側にあり、対策も両側で行います。

SWSの引き金対策アルゴリズム
受信側小さな空きが出るたび窓を更新広告する窓が「MSSか受信バッファの半分」に達するまで0のまま据え置く(窓更新の抑制)
送信側アプリが小さなデータを頻繁にwriteするNagleアルゴリズム:未ACKの小セグメントがある間は小データを溜める

Nagleアルゴリズムの判断規則

送信側の対策が Nagleアルゴリズム です。規則は一行で表せます。

未確認の送信データが存在する間は、
MSS未満のセグメントを送らずバッファに溜める。
(全データがACKされるか、MSS分溜まったら送る)

狙いは小さな書き込みを束ねて1つのフルサイズセグメントに合体させ、ヘッダ比率を下げることです。telnetのような対話型でも、往復の間に溜まった打鍵をまとめて送れます。

NagleとDelayed ACKの致命的な相互作用

受信側には、ACKを少し遅らせてデータやウィンドウ更新に相乗りさせる Delayed ACK(遅延ACK、最大約200ms待つ) があります。これがNagleと噛み合うとデッドロックに近い遅延が生じます。送信側はACKが来るまで次の小データを送らない(Nagle)。受信側は次のデータが来るまでACKを遅らせる(Delayed ACK)。互いが互いを待ち、約200msのストールが「リクエスト→レスポンス」型の通信で表面化します。

この相互作用は、HTTPのような小さなリクエストを送って即レスポンスを待つ通信で特に顕著です。だからレイテンシ重視のアプリは TCP_NODELAY でNagleを無効化するのが定石です。

TCP_NODELAY 有効 → Nagleを切る。小セグメントを即送信。
  対話・RPC・HTTPなど低遅延優先の用途で設定する。
バルク転送(大ファイル)では Nagle を残す方が効率的。

つまりNagleは万能ではなく、スループット最適化(束ねる)とレイテンシ最適化(即送る)のトレードオフ を体現するスイッチです。用途で判断します。

観測のヒント

スケーリングが効いているかは確立時のSYNを見ます。tcpdump -v のオプション欄に wscale N が両方向で出ていれば有効です。ゼロウィンドウやSWS的な細切れは、キャプチャで tcp.analysis.zero_window や極端に小さいセグメント長として現れます。

まとめ

フロー制御は受信側バッファをスライディングウィンドウで保護する仕組みで、ネットワークを守る輻輳制御とは独立し、実効送信窓は両者の最小値でした。16ビット窓の64KB制限は高BDP路で致命的になるため、ウィンドウスケールオプションがハンドシェイク時にだけ左シフト量を交渉し最大1GBへ拡張します。そしてSWS・Nagle・Delayed ACKは「効率」と「遅延」のせめぎ合いとして相互作用し、用途に応じて TCP_NODELAY で調停します。コネクションの状態遷移と合わせて押さえると、観測した転送の遅さを原理から切り分けられるようになります。

試験・面接で問われる急所

「rwndとcwndの違いと、送信窓の決まり方」→ 受信保護とネットワーク保護、実効窓は最小値。「ウィンドウスケールはいつ交渉するか」→ SYN/SYN-ACKのみ、途中変更不可、片方向ごと独立。「SWSの送受の対策」→ 送信側Nagle、受信側は窓更新抑制。「Nagleとdelayed ACKでなぜ遅くなるか」→ 互いに待ち合って約200msストール、対策はTCP_NODELAY。

ネットワーク Article

TCPのフロー制御とウィンドウスケーリングを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

TCP

比較で見る軸

難易度: advanced / カテゴリ: ネットワーク / タグ数: 5

導入後に効く点

16ビットのウィンドウ欄は最大64KBしか表せない。高帯域・長RTT路では足りず、ウィンドウスケールオプションで左シフト量を交渉し最大1GBまで拡張する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
ネットワーク
タグ数
5

判断チェックリスト

  • 自社の用途が「TCP / フロー制御」に近いか確認する。
  • 強みである「フロー制御は受信側のバッファ空きを表す広告ウィンドウ(rwnd)で送信量を絞り、輻輳制御(cwnd)とは別物。実効の送信窓は両者の最小値。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

TCPフロー制御ウィンドウスケーリングNagleトランスポート層TCPフロー制御ウィンドウスケーリング
参考: 公式情報