コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
3-way ハンドシェイク
なぜ 2 往復ではなく、3 回なのか
TCP は通信を始める前に挨拶を交わします。呼びかけて、応答が返ってきた。これで通じることは確認できたように見えます。ところが TCP は、そのあともう 1 回、3 回目を送ります。
無駄に見えるこの 3 回目に、仕組みの本質があります。
確かめたいのは、往復ではなく両方向
呼びかけと応答の 2 回で確認できるのは、呼びかけた側から見た往復だけです。「自分の声が届き、相手の声が返った」ことは分かります。
一方、応答した側は何も確認できていません。自分が返した応答が相手に届いたかどうかを知る手立てがないからです。届いていなければ、相手は待ち続けているかもしれません。そもそも最初の呼びかけ自体が、差出人を偽装した無関係な誰かのものだったかもしれません。
3 回目で、呼びかけた側が「あなたの応答を受け取りました」と伝えます。これで応答した側も、自分から相手への道が通じていることを確認できます。両方が、自分の送信が相手に届くと確認できて初めて、データを流し始められます。2 回では、片側にしか確証が生まれないのです。
ここで確かめているのは、相手が実在するかどうかでもあります。差出人の欄は簡単に偽装できますが、3 回目まで手順を踏めるのは、その番号に返事が実際に届く相手だけです。偽装した差出人を名乗った側には応答が返らないので、3 回目を返せません。
挨拶で交換しているもの
3 回のやりとりで伝えているのは、通じるかどうかだけではありません。もう 1 つ、これから使う通し番号の開始値を互いに教え合っています。
TCP は塊ごとに通し番号を振って順番を管理します。この番号は 0 から始まるわけではなく、接続ごとに違う値から始まります。同じ相手との古い接続の残りかすや、外から適当な番号を投げ込んでくる相手と混ざらないようにするためです。開始値がずれていると、並べ直しも送り直しも成り立ちません。だから 1 回目と 2 回目で、それぞれ自分の開始値を相手に渡します。3 回目は、その値を確かに受け取ったという返事も兼ねています。
番号は塊ごとに 1 ずつではなく、送ったバイト数だけ進みます。100 バイト送れば番号は 100 進みます。だから受け取り側は「何番まで受け取った」と返すだけで、どこまで届いたかを正確に伝えられます。途中で塊の切れ目が変わっても辻褄が合うのは、この数え方のおかげです。
挨拶にも代償がある
3 回のやりとりには、往復 1 回半ぶんの時間がかかります。国内でも数ミリ秒、海外なら 100 ミリ秒を超えることもあります。データを 1 バイトも送らないうちに、この時間が確定で乗ります。
だから短いやりとりを何度も繰り返すアプリは、挨拶の時間だけで遅くなります。逆に言えば、挨拶さえ終われば、同じ接続を使い続けるかぎり追加の代償はありません。接続を張りっぱなしにして使い回す、往復の回数そのものを減らす、といった工夫が Web の高速化で繰り返し出てくるのはこのためです。
もう 1 つの代償が、応答した側の待ち状態です。呼びかけを受けた側は、3 回目が来るまで「途中まで進んだ接続」を覚えておく必要があります。ここを突いて、呼びかけだけを大量に送りつけて 3 回目を返さず、相手の覚えておく領域を埋め尽くす攻撃が成立します。仕組みを理解していないと、なぜそれが効くのかも分かりません。
復習ミニクイズ
TCP の 3-way ハンドシェイクで送信されるフラグの順序として正しいものはどれですか