TCPコネクションを確立したからといって,それだけでデータが間違いなく転送できるわけではない。IPを使ってTCPパケットを送る以上,ネットワークの中でパケットが失われたり順番が入れ替わったりすることがあるからだ。

 でも大丈夫。TCPは,パケットが失われたり順番が変わったことを検出して,再送したり正しい順番に入れ替えるメカニズムを備えている。

返事が無ければ再送する

 パケットが失われた場合,パケットが失われたことを見つけ出して,失われたパケットを再送する必要がある。そこでTCPスタックは,送信相手からTCPパケットが届いたという確認を送り返してもらう(図3-1)。送ったのに返事が来なければ届いていないとみなして,TCPパケットを送り直す。こうすれば,データの抜けはなくなるわけだ。

図3-1●届いたことを応答してもらい,応答がなかった分をもう一度送る
図3-1●届いたことを応答してもらい,応答がなかった分をもう一度送る データを確実に転送するために,受信側が応答することになっている。応答がなければ再送する。

 ここで問題になるのは,元のデータをいくつかのデータに分割して複数のTCPパケットで送る場合。データの大きさが1個のTCPパケットで転送できる量を超えたりすると,連続した複数のTCPパケットを送信することになる。パケットの区別なく「届いた」というだけの返事がきても,どのパケットが届いたかがわからない。

 そこでTCPでは,転送するデータをバイト単位でカウントし,その数値を連絡し合うことで,データがどこまで届いたのかがわかるようにしている。

データの位置を数値で管理する

 具体例で見てみよう(図3-2)。説明をわかりやすくするために,ここでは1個のパケットで100バイトのデータを転送するTCPコネクションを例に考える。

図3-2●シーケンス番号と確認応答番号を使って確実に転送する
図3-2●シーケンス番号と確認応答番号を使って確実に転送する
送信側は確認応答番号を受け取ることで相手に届いたことを確認する。受信側から返事が届かないときは,直前に受け取った確認応答番号から相手に届いていないデータを推測し,再送する。 [画像のクリックで拡大表示]

 ヘッダーに含まれるシーケンス番号は,TCPコネクションができてから切れるまでの間,連続した数値が割り当てられる。シーケンス番号の初期値が1のケースで,送信側が101バイト目から200バイト目までのデータを送るとき,シーケンス番号は「101」となる。

 このパケットをきちんと受け取った受信側は,次に受け取るはずのデータの先頭バイトを確認応答番号として送信する。200バイト目まで受け取ったなら,確認応答番号に「201」と書き込んで送ることになる。送信側がこの「201」の確認応答番号を含むTCPパケットを受け取れば,200バイト目まではきちんと転送できたことがわかる。

 シーケンス番号は,受信側がパケットの順番をそろえるのにも利用される。順番が入れ替わって届いても,シーケンス番号順に並べ替えれば元のデータが正確に再現できる。

時間を待ってまとめて再送する

 ネットワークの中で一つのTCPパケットが消えてしまった場合を考えてみよう。受信側は確認応答番号を更新したTCPパケットを送信できない。そこで送信側は,データを送信したのに一定時間対応する確認応答が受け取れないときは,そのデータが届いていないと判断し,最後に受け取った確認応答番号のところからパケットを再送する。このため,受信側に同じパケットが複数回届くこともある。ちょっと効率は悪いが,重複したパケットは受信側で整理することになっている。

 TCPコネクションではこのようにしてシーケンス番号とそれに対応する確認応答番号で確実にデータを転送する。ただし,データを送るTCPパケット一つずつに確認応答があるとは限らない。連続してパケットを受信したときは,複数のパケットにまとめて確認応答してもよいことになっている。

 実際のTCPコネクションでは,データが双方向に流れる。このため,データを載せたTCPパケットが,逆方向のデータの確認応答を兼ねたりする。また,TCPパケットには,シーケンス番号と確認応答番号の両方が必ず書き込まれることになっているので,実際のやりとりはもう少し入り組んで見える。しかし,基本を押さえたうえで見れば,難しいことをしているわけではないことがわかるはずだ。