TCPの信頼性制御

TCPは何を保証しているのか

[[TCP/IP]] のIP層は「届いたかどうか確認しない」送りっぱなしの配送です。その上でTCPは、確認応答・順序の復元・失われた分の再送を積み重ねて「確実に、順序どおり届く」通信を作り出しています。これを支えるのが再送制御・フロー制御・輻輳制御の3つです。

再送制御 — 届かなければ送り直す

TCPはデータにシーケンス番号(通し番号)を振って送り、受信側は「ここまで受け取った」という確認応答(ACK)を返します。

  • 一定時間ACKが返らなければタイムアウト再送する
  • 同じACKが重複して届いたら「その先が抜けた」と判断し、タイムアウトを待たずにすぐ再送する(高速再転送)
  • 順序が入れ替わって届いても、シーケンス番号を頼りに受信側で並べ直す

[[UDP]] にはこの仕組みが一切なく、その分の軽さと引き換えに「届いたかどうか分からない」プロトコルになっています。

フロー制御 — 受信側の器に合わせる

受信側は「あとどれだけ受け取れるか」(受信ウィンドウ)をACKに載せて伝え、送信側はその範囲までしか送りません。処理の遅い受信側にデータを浴びせてあふれさせない、相手の都合に合わせる制御です。

輻輳制御 — ネットワークの混雑に合わせる

フロー制御が「相手の都合」なら、輻輳制御は「経路(ネットワーク)の都合」です。送信側は混雑の兆候(パケットロスや遅延の増加)を観測しながら、送る量(輻輳ウィンドウ)を調整します。

  • スロースタート — 接続直後は少量から始め、ACKが返るたびに送信量を倍々に増やす
  • 混雑の検知と抑制 — パケットロスを検知したら送信量を大きく絞り、そこから徐々に増やし直す

全員が自分の限界まで送り続けるとネットワーク全体が詰まる(輻輳崩壊)ため、各送信者が自主的にブレーキを踏む設計になっています。インターネットが混雑しても完全には止まらないのは、この制御のおかげです。

接続の確立(3ウェイハンドシェイク)

TCPはデータを送り始める前に、まず接続を確立します。ここで互いに知らせ合うのが、先ほどのシーケンス番号の初期値です。

  1. SYN — クライアントが「これから通信したい。私の番号はここから始める」と伝える
  2. SYN/ACK — サーバーが「受け取った。私の番号はここから始める」と自分の初期値を添えて返す
  3. ACK — クライアントが「そちらの番号も受け取った」と返す

この3手順で双方が相手の番号の起点を知り、以降のACKや並べ直しが噛み合うようになります。

見落としやすいのは、この手続きだけで往復1回分の時間を消費することです。HTTPSならさらにTLSのやり取りが加わり、本来のデータが流れ始めるのはその後です。しかも直後はスロースタートで送信量が絞られています。RTTの大きい相手ほどこの「送る前の待ち」が表示速度に響くため、往復回数や接続の張り直しを減らす工夫が重ねられてきました([[帯域と遅延]])。

初学者向けポイント

  • 「接続直後だけ遅い」のはスロースタートの仕様 — 大きなファイルの転送速度は徐々に上がる
  • [[帯域と遅延]] の遅延(RTT)が大きい回線では、ACKの往復を待つ分だけウィンドウが増えにくく、帯域が余っていても速度が出ないことがある
  • HTTP/3(QUIC)は再送・輻輳制御を [[UDP]] の上に自前で実装し直した設計 — 「TCPの仕組みを知る」ことはQUICの理解にも直結する

関連技術とのつながり

  • [[TCP/IP]] — 本記事はトランスポート層のTCPの内部動作の深掘り
  • [[UDP]] — 3つの制御をすべて省いた対極の設計
  • [[帯域と遅延]] — RTTとウィンドウサイズがスループットの上限を決める
  • [[ソケット通信]] — アプリはソケット越しにこの制御の恩恵を意識せず受けている
  • [[HTTP/2とHTTP/3]] — HTTP/2はTCP上で多重化し、HTTP/3はTCPを離れてUDP上のQUICへ移した
  • [[エラーハンドリングとリトライ設計]] — TCPが保証するのは到達まで。処理の再試行はアプリの仕事
Q: TCPの再送制御の説明として正しいのはどれ?
- [x] シーケンス番号とACKで届いたことを確認し、届かなければ送り直す
- [ ] すべてのデータを常に2回ずつ送って信頼性を上げる
- [ ] 受信側が足りない分を自力で復元する
解説: TCPは通し番号と確認応答で抜けを検出し、タイムアウトや重複ACKをきっかけに失われた分だけを再送します。

Q: フロー制御と輻輳制御の違いとして正しいのはどれ?
- [ ] フロー制御は暗号化、輻輳制御は圧縮の仕組み
- [x] フロー制御は受信側の処理能力に、輻輳制御はネットワークの混雑に合わせる制御
- [ ] どちらも同じ仕組みの別名
解説: フロー制御は受信ウィンドウで「相手の都合」に、輻輳制御は輻輳ウィンドウで「経路の都合」に合わせます。合わせる対象が異なる別々の制御です。

Q: スロースタートの動作として正しいのはどれ?
- [ ] 接続直後から回線の最大速度で送り始める
- [x] 少量から送り始め、ACKが返るたびに送信量を増やしていく
- [ ] 常に一定量だけを送り続ける
解説: 接続直後は経路の空き具合が分からないため少量から始め、様子を見ながら送信量を増やします。「接続直後だけ遅い」のはこの仕様です。