フロー制御
受信側の処理能力やバッファの空きに合わせて、送信側が送るデータの量や速さを調整する通信制御。受信側があふれてデータを失うことを防ぐもので、ネットワーク全体の混雑を抑える輻輳制御とは目的が異なる。
イマシルちゃんの解説
送る側のコンピュータが速くて、受け取る側がゆっくりしか処理できないと、届いたデータを置いておく場所がいっぱいになっちゃう。
あふれたデータは捨てられて、送り直しが必要になるんだ。
そこで受け取る側が「今はここまでなら受け取れるよ」「少し待って」と送る側に伝えるの。
こうして送る量を相手に合わせて調整する通信制御をフロー制御というよ。
TCPでは受け取れる量を「ウィンドウ」として知らせ、イーサネットでは一時停止を頼むPAUSEフレームを送るんだ。
相手のペースに合わせて送るから、むだな送り直しを減らせるんだね。
この解説はわかりやすかったですか?
より深い解説
目的と位置付け
通信では、送信側と受信側の処理速度が同じとは限らない。高性能なサーバが低速な端末へ送る場合や、受信側のアプリケーションが一時的に忙しい場合には、受信側の一時記憶領域がいっぱいになる。そのまま送り続けると、あふれたデータは捨てられ、再送が必要になる。フロー制御は、受信側の状態を送信側へ伝え、送る量を受信側が処理できる範囲に抑える仕組みである。
フロー制御は特定の一つのプロトコルの名前ではない。さまざまな層で、それぞれの方法によって実現されている。
| 層・場面 | 代表的な方法 | 制御する範囲 |
|---|---|---|
| シリアル通信 | RTS/CTS信号、XON/XOFF文字 | 直接つながった2台の機器間 |
| データリンク層 | HDLCのRNR、イーサネットのPAUSEフレーム | 隣接する機器間(ホップごと) |
| トランスポート層 | TCPの受信ウィンドウ、QUICのフロー制御 | 通信する端末どうし(エンドツーエンド) |
| アプリケーション層 | HTTP/2のウィンドウ更新 | ストリームやコネクションごと |
データリンク層のフロー制御は、隣り合う機器の間だけで働く。経路の途中にある各機器が、自分の受信側の状態を手前の機器に伝える。トランスポート層のフロー制御は、途中の機器に関係なく、最終的にデータを受け取る端末の状態を送信元に伝える。
基本となる方式
フロー制御の方式は、大きく分けると次のとおりである。
| 方式 | 動作 | 特徴 |
|---|---|---|
| ストップアンドウェイト | 1つ送るたびに確認応答を待つ | 単純だが、遅延が大きいと効率が悪い |
| スライディングウィンドウ | 確認応答を待たずに一定量まで続けて送る | 確認応答が届いた分だけ送信可能な範囲をずらす |
| オン・オフ制御 | 受信側が停止と再開を指示する | XON/XOFFやPAUSEフレームが該当する |
| クレジット方式 | 受信側が受け入れ可能な量(クレジット)を与える | 送信側はクレジットの範囲で送る |
表の2行目の方式では、確認応答を受け取った分だけ送信可能な範囲を前にずらし、続きを送る。複数のデータをまとめて送れるため、一つずつ確認する方式より回線を効率よく使える。
計算例:待ち時間と効率
10Mbit/sの回線で、1,000オクテットのフレームを送る場合を考える。1フレームの送信時間は次のとおりである。
1,000 × 8 ÷ 10,000,000 = 0.0008秒 = 0.8ms
フレームを送り終えてから確認応答が戻るまでに9.2msかかるとする。ストップアンドウェイトでは、1フレームあたり0.8 + 9.2 = 10msの周期になる。
実効速度 = 8,000ビット ÷ 0.01秒 = 800kbit/s
回線の使用効率 = 0.8ms ÷ 10ms = 8%
ウィンドウを5フレームにすると、1周期で5フレーム送れるので実効速度は4Mbit/sになる。回線を休みなく使うには、10ms ÷ 0.8ms = 12.5なので、13フレーム以上のウィンドウが必要になる。遅延が大きい経路ほど、大きなウィンドウが必要になるわけである。
TCPのフロー制御
TCPでは、受信側が受け入れ可能なデータ量を、TCPヘッダーのウィンドウサイズ欄に入れて通知する。この値を受信ウィンドウ(rwnd)という。送信側は、確認応答を待っているデータの量が受信ウィンドウを超えないように送る。仕様はRFC 9293で定められている。
実際に送れる量は、受信側が通知する受信ウィンドウと、送信側が輻輳を推定して決める輻輳ウィンドウ(cwnd)の小さいほうで決まる。次の独自例で確認する。
| 項目 | 値 |
|---|---|
| 受信ウィンドウ | 32,000オクテット |
| 輻輳ウィンドウ | 48,000オクテット |
| 送信済みで確認応答待ちのデータ | 20,000オクテット |
送信の上限は32,000オクテットになり、追加で送れるのは32,000 − 20,000 = 12,000オクテットである。
受信側のアプリケーションがデータを読み出さないと、受信バッファが埋まり、ウィンドウサイズ0が通知される。これをゼロウィンドウという。送信側は送信を止めるが、ウィンドウが開いたことを知らせる通知が失われると、互いに待ち続けてしまう。そこで送信側は持続タイマーを使い、定期的に小さなセグメント(ウィンドウプローブ)を送って状態を確かめる。
ウィンドウサイズ欄は16ビットのため、そのままでは65,535オクテットまでしか通知できない。高速で遅延の大きい経路では、RFC 7323のウィンドウスケールオプションで値を拡大する。また、受信側がわずかな空きのたびに小さなウィンドウを通知すると、小さなセグメントばかりが送られて効率が落ちる。これをシリーウィンドウシンドロームといい、送受信の双方で小さな通知や送信を控える対策がとられている。
イーサネットのPAUSEフレーム
イーサネットでは、全二重の接続で受信側が送信側に一時停止を求めるPAUSEフレームが使われる。もとはIEEE 802.3xとして1997年に追加され、現在はIEEE 802.3の本体に統合されている。半二重の接続では使えない。
| 項目 | 内容 |
|---|---|
| 種類 | MAC制御フレーム(タイプ値0x8808) |
| 宛先MACアドレス | 予約されたマルチキャストアドレス 01-80-C2-00-00-01 |
| オペコード | 0x0001 |
| 停止時間(pause_time) | 16ビット。単位は512ビット時間 |
停止時間は、そのリンクで一定のビット数を送る時間を単位として指定する。1Gbit/sでは1ビットの送信に1nsかかるので、1単位は512nsになる。最大値65,535を指定したときの停止時間は次のとおりである。
65,535 × 512ns = 33,553,920ns ≒ 33.6ms
10Gbit/sでは同じ値でも約3.36msになる。停止時間に0を入れたPAUSEフレームを送ると、待たずに送信の再開を求められる。PAUSEを使うかどうかは、オートネゴシエーションで相手と取り決めることができる。
PAUSEフレームはリンク上のすべての通信を止める。そのため、音声などの急ぎの通信まで巻き添えで待たされる。スイッチが受けた停止を手前へ次々に伝えると、関係のない端末の通信まで止まることがある。一般的なLANでは、通信の損失をTCPの再送と輻輳制御に任せ、PAUSEを使わない設計も多い。
データセンターでは、FCoEなどパケットの損失を許さない通信のために、優先度ごとに停止できるPFC(Priority-based Flow Control)が使われる。IEEE 802.1Qbbで定められ、8つの優先度ごとに停止と再開を制御できる。止めたい優先度の通信だけを止め、ほかの通信は流し続けられる。
なお、半二重のポートで衝突を意図的に起こすなどして相手の送信を抑える機能を、バックプレッシャーと呼ぶ製品がある。これは製品の機能であり、PAUSEフレームとは別の仕組みである。
シリアル通信とHDLCのフロー制御
RS-232Cなどのシリアル通信では、2種類の方法がある。信号線を使うハードウェアフロー制御では、RTS(送信要求)とCTS(送信可)の信号で、送ってよいかを伝える。制御文字を使うソフトウェアフロー制御では、データの中にXOFF(DC3、0x13)を送って停止を求め、XON(DC1、0x11)で再開させる。
ソフトウェアフロー制御は信号線が少なくて済む。ただし、送りたいデータの中に同じ値のバイトが含まれると、停止や再開の指示と誤って解釈される。バイナリデータを送る場合は、信号線を使う方式を選ぶ。
HDLCでは、受信側が受信の準備ができていないことをRNR(Receive Not Ready)で知らせ、準備ができたらRR(Receive Ready)で再開を伝える。
HTTP/2とQUICのフロー制御
HTTP/2は、一つのTCPコネクションの中で複数のストリームを同時に扱う。TCPのフロー制御はコネクション全体にしか効かないため、HTTP/2はストリームごとにも独自のフロー制御を持つ。受信側はWINDOW_UPDATEフレームで受け入れ可能な量を追加する。初期値は65,535オクテットである(RFC 9113)。トランスポート層で動作するQUICでも、コネクション全体とストリームごとの上限をMAX_DATAやMAX_STREAM_DATAで通知する(RFC 9000)。
混雑を抑える制御との違い
フロー制御と輻輳制御は、どちらも送信量を調整する。しかし、何を守るかが異なる。
| 観点 | フロー制御 | 輻輳制御 |
|---|---|---|
| 守る対象 | 受信側の端末や機器 | 経路上のネットワーク全体 |
| 判断の材料 | 受信側からの明示的な通知 | パケットの損失や遅延の増加から推定する |
| TCPでの値 | 受信ウィンドウ(rwnd) | 輻輳ウィンドウ(cwnd) |
| 値を決める側 | 受信側 | 送信側 |
受信側が十分に速くても、経路の途中が混雑していれば輻輳制御が送信量を絞る。逆にネットワークが空いていても、受信側のバッファが小さければフロー制御が上限になる。午前問題では、この二つの説明を入れ替えた誤りの選択肢がよく使われる。
設計と障害の切り分け
フロー制御が原因で性能が出ない例は多い。主なものを示す。
| 症状 | 確認すること |
|---|---|
| 回線は空いているのにTCPのスループットが低い | 受信バッファとウィンドウスケールの設定、RTT |
| パケットキャプチャでゼロウィンドウが続く | 受信側のアプリケーションやディスクの処理が追いついているか |
| スイッチのPAUSE受信カウンタが増え続ける | 接続先の機器の処理能力、PAUSE設定の不一致 |
| 一部の端末の不調でほかの通信まで遅くなる | PAUSEの連鎖、PFCの対象とする優先度の設計 |
Wiresharkなどのパケットキャプチャでは、ゼロウィンドウやウィンドウの更新が記録される。受信側がウィンドウを0にしている場合、遅い原因はネットワークではなく受信側の処理にある。ネットワーク機器を増強しても改善しない。
午後での問われ方
- TCPのウィンドウサイズとRTTから、1本のコネクションで出せるスループットの上限を計算させる
- 通信が遅い原因を、パケットキャプチャのウィンドウサイズの変化から読み取らせる
- フロー制御と輻輳制御のどちらが働いているかを、状況から判断させる
- データセンターの構成で、損失を許さない通信のためにPFCを使う理由と、その副作用を説明させる