TCPパケットを実際に見ながら理解する:3ウェイ/4ウェイハンドシェイクとデータ転送の流れ
概要
この記事は、TCP通信についての理解を深めることを目的とし、実際のTCPパケットを観察していきます。
TCP通信においてどんなやり取りが交わされているのか、パケット一つひとつを「人間の会話」に例えたりしながら見ていきたいと思います。
具体的には、以下の3つのセクションに分けて観察していきます。
- 3ウェイハンドシェイク(接続開始のやり取り)
- データ転送
- 4ウェイハンドシェイク(接続終了のやり取り)
TCP通信の仕組みを理解するための重要な要素
実際にパケットを観察する前に、TCP通信における基本概念を整理しておきます。
フラグ: 通信の意図を伝える信号
TCPのフラグは、パケットに込められた通信の意図を示すための信号のようなものです。送信側が「接続を始めたい」「データを受信したことを伝えたい」「もう通信を終わりにしたい」など、状況ごとの意思表示をフラグで行います。
このフラグを理解することで、各パケットがどんな意味を持っているのかが見えてきます。代表的なフラグとしては、SYN,ACK,FINなどがあります。
フラグ | 意味 | 話し言葉での例え |
---|---|---|
SYN | 接続開始要求 | 「通信させてください!」 |
ACK | 受信確認 | 「受け取りました!」 |
FIN | 接続終了要求 | 「もう送るものはないです!」 |
TCPパケットを観察するために利用するWireshark
というツール(後述します)では、これらのフラグを「Flags」という項目で確認できます。
シーケンス番号: データの順番を管理する仕組み
TCPでは、大きなデータをそのまま送るのではなく、MSS(Maximum Segment Size)という単位に分割して送信します。このかたまりを「セグメント」と呼びます。
送信側は、それぞれのセグメントに「何番目のデータか」を示す シーケンス番号(Sequence Number) を付けて送信します。
これは、受信側が正しい順番でデータを再構成できるようにするための通し番号のようなものです。途中でパケットが遅延・再送された場合でも、この番号があることで正しく並べ直すことができます。
ACK番号: どこまでデータが届いたかを伝えるための番号
ACK番号(Acknowledgment Number)は、受信側が送信側に対して「データをXXXまで受信したよ!」と伝えるための番号です。XXXには次に送信側に送ってほしいシーケンス番号を指定します。
つまり、「XXXよりも前のデータはちゃんと届いてるから、次はXXXから送ってね」と相手に伝えるわけです。
パケットキャプチャツール: Wiresharkの使い方
さて、TCPパケットを観察する前にもう1つだけ準備をします。パケットを観察するためにWiresharkというアプリケーションをインストールしておきます。
詳細な使い方は割愛しますが、以下のように特定の通信に絞ると観察しやすいです。
まず、接続開始のパケット(SYNフラグが立っている)を検索します。
tcp.flags.syn == 1
さらに特定の通信だけに絞って、流れをしっかり観察するためにストリーム番号で絞って検索します。
tcp.stream == 73
これで通信開始から終了までの一連の通信を観察しやすくなります。
TCPパケットを観察して通信の流れを追う
接続開始: スリーウェイハンドシェイク
スリーウェイハンドシェイクとは、TCP通信においてコネクション(接続)を確立するための手順です。通信の信頼性を確保するため、クライアントとサーバ間で3回のパケット送受信を行います。
具体的には、次の3つのやりとりによって接続が確立されます:
- クライアント → サーバ に SYN(接続開始要求)を送信
- サーバ → クライアント に SYN + ACK(接続開始応答)を返す
- クライアント → サーバ に ACK(応答確認)を返す
この3ステップを通して、お互いに「通信を始めたい」「OK、始めましょう」と確認し合うことで、TCPの接続が確立されます。
以下はスリーウェイハンドシェイクを示す3つのパケットです。
No. | 送信元 → 宛先 | Seq | Ack | Len | フラグ | 通信の意味 |
---|---|---|---|---|---|---|
1 | クライアント → サーバ | 0 | 0 | 0 | SYN | 接続開始要求 |
2 | サーバ → クライアント | 0 | 1 | 0 | SYN, ACK | 接続開始応答 |
3 | クライアント → サーバ | 1 | 1 | 0 | ACK | 接続確立完了 |
以下はスリーウェイハンドシェイクを図解したものです。
以下は、Wireshark上の接続開始要求のパケットです。
データ転送
スリーウェイハンドシェイクによって接続が確立されると、実際のデータ送受信が始まります。ここでは、サーバとクライアントが交互にデータをやり取りしているTCP通信の例を見ていきます。
No. | 送信元 → 宛先 | Seq | Ack | Len | フラグ | 通信の意味 |
---|---|---|---|---|---|---|
4 | サーバ → クライアント | 1209 | 518 | 1208 | PSH, ACK | TLS サーバからのデータ送信 |
5 | サーバ → クライアント | 2417 | 518 | 765 | PSH, ACK | TLS サーバからのデータ送信の続き |
6 | クライアント → サーバ | 518 | 3182 | 0 | ACK | サーバからのデータをすべて受信完了 |
7 | クライアント → サーバ | 518 | 3182 | 64 | PSH, ACK | TLS クライアントからのデータ送信 |
8 | クライアント → サーバ | 582 | 3182 | 359 | PSH, ACK | TLS クライアントからのデータ送信の続き |
9 | サーバ → クライアント | 3182 | 941 | 0 | ACK | クライアントのデータ受信確認 |
このやり取りでは、次のような流れでパケットが交わされています:
- サーバが2つのセグメントに分けてデータ送信(No.4, No.5)
- クライアントが「3181まで受信済みなので、次は3182から送ってください」とまとめてACK応答(No.6)
- クライアントが自分のデータを2回に分けて送信(No.7, No.8)
- サーバが「940まで受信済みなので、次は941から送ってください」とまとめてACK応答(No.9)
このように、"データ送信"と"どこまで受信したかの応答"を繰り返すことで、送信したいデータを抜け漏れなく届けることができます。
以下はこの流れを図解したものです。
TCPデータ転送の仕組み:ACK番号の意味と応答の最適化
まず注目したいのは、ACK番号が受信済みの最後のシーケンス番号 + 受信データサイズになっていることです。
たとえば、サーバからの送信されたNo.4,5のパケットを見てみます。
- No.4: Seq: 1209 / Len: 1208
- No.5: Seq: 2417 / Len: 765
この2つのパケットで送られたデータは以下の範囲になります。
- 1209〜2416(1208バイト)
- 2417〜3181(765バイト)
※ Seq番号はデータの先頭位置、Lenがバイト数なので、「Seq番号〜Seq番号+Len-1」の範囲のデータが送信されます。
クライアントがこれらすべてを受け取った場合、「3181まで受け取りましたよ。次は3182以降を送ってくださいね」という意味を込めて、次に欲しいデータの先頭である3182をACK番号として返します。その応答を示すパケットがNo.6のパケットです。
次に注目したいのは、複数のセグメントに対して、まとめてACK確認応答をしている点です。
すべてのパケットにACK応答していると、パケットの数が増えて通信効率が落ちてしまいます。そこで、TCPでは一定のデータ量(ウィンドウサイズ)までは、ACKを待たずにまとめて送信するという最適化が行われています。
接続終了: 4ウェイハンドシェイク
最後に、TCP通信の接続終了時にどのようなやり取りが行われるのかを確認してみます。
TCPの接続終了は、4ウェイハンドシェイク(Four-way Handshake) と呼ばれるプロセスによって行われます。これは、お互いに通信の終了を宣言し、それをお互いに確認し合うことで、安全に接続を閉じる仕組みです。
- FIN(終了要求) — 「もう送るものはありません」と宣言
- ACK(終了の受理) — 相手がそれを確認
- FIN(終了要求) — もう片方も「こちらも送るものはない」と宣言
- ACK(終了の受理) — 相手がそれを確認。この確認で接続が完全にクローズ
今回、実際に確認した接続終了のやりとりは以下の通りです。
No. | 送信元 → 宛先 | フラグ | 通信の意味 |
---|---|---|---|
10 | クライアント → サーバ | FIN, ACK | 「これまでの受信OKです」+「自分はもう送るものはないです」 |
11 | サーバ → クライアント | FIN, ACK | 「あなたのFIN、受け取りました」+「こちらも終了します」 |
12 | クライアント → サーバ | ACK | 「あなたのFIN、受け取りました」(接続完全にクローズ) |
実際に確認されたやり取りでは、サーバからの ACK+FIN が1つのパケットにまとめられて送信されており、3パケットで完結しています。これは、TCP通信においてよくある最適化の1つで、フラグをまとめて送信することで通信回数を減らしています。
図解すると以下の通りです。
最後に
ネットワーク系には少し苦手意識がありましたが、実際のTCPパケットを観察してみると、仕組みが見えてきて腑に落ちる部分もありました。書籍を読むだけでは、どうしても情報が左から右に流れていくだけでしたが、パケットを実際に追うことで、少しイメージが掴めた気がします。
同じように感じている方の、何かしらの理解の助けになれば幸いです。
Discussion