合同会社小村ソフト
第 2 章

コネクション — TCP の 3 ウェイハンドシェイクと切断

コネクションの正体は両端が合意した番号の数え始め。SYN → SYN/ACK → ACK がなぜ 3 回必要なのか、FIN と TIME_WAIT で何を守っているのかを追う。

「コネクションを張る」の正体は何か

connect() が返ってくれば繋がった、くらいの感覚で普段は困りません。でも障害のときは「繋がらない」の中身 — 拒否されたのか、応答が無いのか、確立後に切れたのか — を区別できないと前に進めません。そのためには「コネクションを張る」が物理的には何なのかを知る必要があります。

答えは意外と地味です。経路上に専用線が確保されるわけではなく、途中のルータは何も覚えません。コネクションとは、両端の 2 台がそれぞれメモリ上に持つ「この 4 つ組の相手と、どの番号までやり取りしたか」という帳簿の対にすぎません。だからコネクションを作る儀式でやるべきことも 1 つだけ — お互いの「数え始めの番号」を伝え合い、確かに聞いたと確認し合うことです。

この数え始めの番号を初期シーケンス番号(ISN)と呼びます。1 から数え始めない理由は第 5 問の TIME_WAIT とも通じます — 直前まで同じ 4 つ組で通信していた古いパケットと紛れないよう、毎回予測しにくい値から始めるのです。

クライアントとサーバの間で SYN、SYN/ACK、ACK の 3 パケットが往復し、両者の初期シーケンス番号が確認されて ESTABLISHED になるシーケンス図

3 ウェイハンドシェイク。ISN はクライアント 5000、サーバ 9000 の例です。ack 番号が常に「相手の番号 + 1 = 次に欲しい番号」になっていることを確かめてください。

3 つのパケットを 1 つずつ読む

クライアントの ISN を 5000、サーバの ISN を 9000 とした例で追います。

1. SYN(クライアント → サーバ)
seq = 5000。「接続したい。私は 5000 から数え始める」。SYN はデータを運ばないが、番号を 1 つ消費する
2. SYN/ACK(サーバ → クライアント)
seq = 9000, ack = 5001。「5001 まで聞いた(= あなたの SYN を受け取った)。私は 9000 から数え始める」— 確認と宣言の相乗り
3. ACK(クライアント → サーバ)
seq = 5001, ack = 9001。「あなたの数え始めも聞いた」。ここで両者とも ESTABLISHED になり、データを送れる

なぜ 3 回か。TCP は双方向に独立した 2 本のバイトストリームを持つので、方向ごとに「数え始めの宣言(SYN)」と「聞いたという確認(ACK)」が必要です。役割は合計 4 つですが、真ん中の 2 つ(サーバの ACK とサーバの SYN)は 1 パケットに相乗りできるので、最小で 3 パケット。2 回では「サーバの宣言が届いた」ことを誰も確認しておらず、4 回は無駄が 1 つある、というわけです。

小問 2-1 — 3 ウェイハンドシェイクを 1 パケットずつ

3 つのパケットそれぞれの「言っていること」を確認します。番号の問題は紙に書いて計算してください。

Q1. 3 ウェイハンドシェイクの 2 番目のパケット(サーバが返す SYN/ACK)が言っていることとして正しいものはどれですか。

Q2. クライアントが初期シーケンス番号 ISN = 5000 で SYN を送りました。サーバが返す SYN/ACK の ack 番号はいくつですか。(SYN はデータを運ばないが、シーケンス番号を 1 つ消費する)

Q3. ハンドシェイクが 2 回(SYN → SYN/ACK で終わり)では成立しない理由として正しいものはどれですか。

切断は 4 役 — 片方向ずつ店じまいする

切断が確立より 1 手多いのは、相乗りできるとは限らないからです。「もう送るものがない」という宣言 FIN は、自分から相手への方向だけを閉じます。相手にはまだ送り残しがあるかもしれないので、FIN を送った後も受信は続けられます(ハーフクローズ)。双方向それぞれに FIN と ACK が要るので、基本形は 4 パケットです。

そして先に FIN を送った側は、すべて終わった後も TIME_WAIT 状態でしばらく(多くの実装で 30 秒〜数分)残ります。理由は 2 つあります。

最後の ACK の保険
自分が返した最後の ACK が消失すると、相手は FIN を再送してくる。応答する係が残っていないと相手は正しく閉じられない
古いセグメントの毒抜き
ネットワーク上をまだ漂っているこのコネクションのセグメントが寿命で消えるまで、同じ 4 つ組の再利用を止める。新しいコネクションに昔のデータが混入する事故を防ぐ

もう 1 つ、行儀の悪い終わり方が RST(リセット)です。「そんなコネクションは知らない/今すぐ壊す」という一方的な通告で、待ち受けのないポートへの SYN、クラッシュしたプロセス宛のデータなどに対して返ります。FIN は合意の店じまい、RST は問答無用の破棄 — キャプチャでこの 2 つを見分けられるだけで、障害の景色は大きく変わります。

小問 2-2 — 切断と TIME_WAIT、そして RST

始まりより終わりのほうが奥深いのが TCP です。実務で出会う頻度も切断側のほうが上です。

Q4. TCP の切断で FIN が双方向に 1 回ずつ(合計 2 回、それぞれに ACK)必要な理由はどれですか。

Q5. 先に FIN を送って切断した側は、すぐには消えず TIME_WAIT 状態でしばらく待ちます。この待ち時間の目的として正しいものはどれですか。

Q6. 誰も待ち受けていないポートへ SYN を送った場合と、ファイアウォールがパケットを黙って捨てる場合の、クライアント側から見た違いとして正しいものはどれですか。

この章で持ち帰ること

  • コネクションの正体は両端が持つ帳簿の対。確立とは「数え始めの番号(ISN)」の合意
  • ハンドシェイクは双方向分の SYN + ACK の 4 役を 3 パケットに相乗りさせた最小形。ack は常に「次に欲しい番号」
  • 切断は FIN で片方向ずつ。TIME_WAIT は故障ではなく、最後の ACK の保険と古いセグメントの毒抜き
  • RST 即返し = 届いた上で拒否、無応答タイムアウト = そもそも応答が返らない。切り分けの第一歩

次章では、確立した後の主役 — シーケンス番号と ACK 番号がデータ転送でどう動くか — を手計算で追います。