QUICをゆっくり解説(8):フロー制御
2021年09月15日 水曜日
CONTENTS
前回、QUICのストリームについて説明しました。今回は、ストリームのフロー制御についてお話しします。フロー制御はそれほど難しい機能ではないので、今回はほとんど用語整理のような内容になっております。
フロー制御と輻輳制御
ネットワーク通信の勉強をしていると、フロー制御と輻輳制御という用語を目にすることがあるかと思います。この2つは同一視されがちです。かくゆう僕も、長い間両者は同じ機能だと間違って理解していました。フロー制御と輻輳制御は、以下のように異なる仕組みです。
- フロー制御:受信者の受信バッファが溢れないようにするための仕組みです。受信者が送信者に何らかの指示をし、送信者はその指示にしたがって送信するデータ量を調整します。
- 輻輳制御:途中のネットワークが溢れないようにするための仕組みです。送信者は、なんらかの方法でパケットの欠落を検知します。パケットの欠落の有無によって、送信するデータ量を調整します。
実際に実装してみると分かりますが、フロー制御の実装はそれほど難しくありません。相手が許容する範囲でデータを送ればよいだけです。一方、輻輳制御の実装は、たとえ擬似コードが示されていたとしても、とても難しいです。輻輳制御については別の回で説明します。
QUICのフロー制御
QUICには3つのフロー制御があります。これらは HTTP/2に由来しています。
- ストリームのフロー制御:それぞれのストリームに対する個別のフロー制御です。受信者は、ストリームの受信可能累積バイト数をMAX_STREAM_DATAフレームに入れて送信者に伝えます。
- コネクションのフロー制御:コネクション内にある全ストリームに対するフロー制御です。受信者は、コネクションの受信可能累積バイト数をMAX_DATAフレームに入れて送信者に伝えます。
- ストリーム数の制御:受信者は、1つのコネクションに対して作成してよい累積ストリーム数をMAX_STREAMSフレームに入れて送信者に伝えます。作成してよいストリームIDの最大値は、「max_streams * 4 + ストリームの種類ごとの最初の値」という計算で算出します。
「相手から伝えられた受信可能累積バイト数」から「自分で管理している現時点での送信バイト数」を引けば、送信してよいバイト数が算出できます。この値が0になると、送信者は送信をやめなければなりません。いわゆるブロックされた状態になります。
送信者がフロー制御によってブロックされた場合は、上記3つのそれぞれの場合に対して以下のフレームを送り、ブロックされたことを受信者に通知できます(送るのは必須ではありません)。
- STREAM_DATA_BLOCKED
- DATA_BLOCKED
- STREAMS_BLOCKED
これらを受け取った受信者は、送信者側に送られるべきデータが存在していることが分かります。上記すべてのフレームは、伝搬中に欠落する可能性があるので、複数回送信してもよいことになっています。
送信者が、ストリームあるいはコネクションのバイト数の制限を守らず、過剰にデータを送信した場合、受信者は FLOW_CONTROL_ERROR としてコネクションを切ります。また、ストリーム数の制限を守らなかった場合は、STREAM_LIMIT_ERROR としてコネクションを切ります。コネクションの終了方法は、次回解説する予定です。
ウインドウとクレジット
TCPのフロー制御はウインドウに基づくと言われます。また、RFC7540では、HTTP/2のフロー制御はクレジットに基づくと説明されます。ウインドウとクレジットは、違う概念なのでしょうか?
QUIC実装者のコミュニティで質問したところ、クレジットという用語は、“Data And Computer Communications”という書籍が広めたと考えられるとのことでした。この本では、フロー制御を3つに大別しています。
- バイナリ方式:受信者は受信データが多過ぎることを1ビットで表現して送信者に伝え、送信者は送信量を少なくする
- クレジット方式:受信者が実際に受け取れるバイト数やパケット数を送信者に伝え、送信者はその制限を守って送信する
- レート方式:受信者が実際に受け取れるレートを送信者に伝え、送信者はその制限を守って送信する
HTTP/2とQUICのフロー制御と同様に、TCPのフロー制御であるウインドウもクレジット方式の一種です。以下の図を見てください。
- TCPでは、現在の受信可能バイト数(ウインドウ)を伝える
- HTTP/2では、受信可能バイト数の差分を伝える
- QUICでは、受信可能累積バイト数を伝える