TCP は、データを送り始める前にコネクションを確立し、データを送り終えたらコネクションを終了します。どちらも、TCP ヘッダのフラグ(SYN、ACK、FIN)を立てたパケットのやり取りで行います。
この記事では、コネクションの確立(3-way handshake)と終了のシーケンスを、Wireshark で見た TCP フラグと一緒に確認します。
- 3-way handshake(SYN、SYN/ACK、ACK)でコネクションを確立する流れ
- FIN と ACK でコネクションを終了する流れ(3回の場合と4回の場合)
- Wireshark で TCP フラグを確認する方法
TCPのフラグ
TCP ヘッダには、そのパケットの役割を示すフラグがあります。コネクションの確立と終了で使うのは次の3つです。Wireshark では、TCP ヘッダの Flags を開くと、どのフラグが立っているか(Set)を確認できます。
| フラグ | 意味 | 使う場面 |
|---|---|---|
| SYN(Synchronize) | コネクションを始めたい。シーケンス番号の初期値を相手に伝える | コネクションの確立 |
| ACK(Acknowledgment) | 相手から受け取ったことを確認する。ACK 番号が有効になる | コネクションの確立後は、すべてのパケットで立つ |
| FIN(Finish) | 送るデータがもうないので、コネクションを閉じたい | コネクションの終了 |
TCPコネクション開始時
TCP では、データを送受信する前にコネクションを確立します。3回のやり取りで確立するので、3-way handshake(スリーウェイハンドシェイク)と呼ばれます。
クライアントが SYN を送る
まず、通信を始めたい側(クライアント)が、SYN フラグを立てたパケットを送ります。「TCP 通信を始めたい」という合図です。
サーバが SYN/ACK を返す
サーバは、クライアントからの SYN を受け入れる場合、ACK フラグを立てて返します。また、サーバ側からもクライアントへの通信を始めるため、SYN フラグも立てる必要があります。この2つを1つのパケットにまとめ、SYN/ACK として返します。
クライアントが ACK を返す
クライアントは、サーバからの SYN を受け入れる場合、ACK フラグを立てて返します。この3回のやり取りで、コネクションが確立され、データを送れるようになります。
この記事の画面は、実際の画面ではなく、検証時のパケットキャプチャから見るポイントを抜き出して描いたイメージです。バージョンによって、項目の並びや表記は変わります。
1▾ Flags: 0x002 (SYN) 000. .... .... = Reserved: Not set ...0 .... .... = Nonce: Not set .... 0... .... = Congestion Window Reduced (CWR): Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...0 .... = Acknowledgment: Not set .... .... 0... = Push: Not set .... .... .0.. = Reset: Not set .... .... ..1. = Syn: Set .... .... ...0 = Fin: Not set
2▾ Flags: 0x012 (SYN, ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Nonce: Not set .... 0... .... = Congestion Window Reduced (CWR): Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 0... = Push: Not set .... .... .0.. = Reset: Not set .... .... ..1. = Syn: Set .... .... ...0 = Fin: Not set
3▾ Flags: 0x010 (ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Nonce: Not set .... 0... .... = Congestion Window Reduced (CWR): Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 0... = Push: Not set .... .... .0.. = Reset: Not set .... .... ..0. = Syn: Not set .... .... ...0 = Fin: Not set
| No. | 表示 | 意味 |
|---|---|---|
| 1 | Flags: 0x002 (SYN) | SYN だけが立っている。クライアントからの最初のパケット |
| 2 | Flags: 0x012 (SYN, ACK) | ACK と SYN の両方が立っている。サーバからの応答 |
| 3 | Flags: 0x010 (ACK) | ACK だけが立っている。これで確立が終わる |
SYN を送るときには、それぞれの側がシーケンス番号の初期値を相手に伝えます。シーケンス番号と ACK 番号の増え方は、【TCP】シーケンス番号とACK番号について解説 で詳しく説明しています。
TCPコネクション終了時
コネクションを閉じるときは、閉じたい側から FIN フラグを立てて送ります。TCP は送信と受信の2つの方向を別々に閉じるので、両方の側が FIN を送り、相手がそれぞれ ACK を返して、コネクションが終わります。先に FIN を送るのはクライアントとは限らず、サーバから閉じることもあります。
RFC(RFC 9293)で示されている基本の形は、FIN と ACK を別々に送る4回のやり取りです(後述)。ただし、サーバが FIN を送るまでの間が短いと、ACK と FIN が1つのパケットにまとまり、3回のやり取りになります。この記事の検証では3回になっていたので、まずこちらを説明します。
クライアントが FIN を送る
コネクションを閉じたい側(ここではクライアント)が、FIN フラグを立てて送ります。コネクションの確立後に送るパケットには、必ず ACK フラグも立てる決まりがあるため、Wireshark では FIN/ACK(0x011)と表示されます。この ACK は、それまでに受け取ったデータへの確認応答で、「FIN と ACK の2つを送った」という意味ではありません。
サーバが FIN/ACK を返す
サーバは、クライアントからの FIN を受け取ったことを ACK で知らせます(ACK 番号は、クライアントの FIN のシーケンス番号+1)。この検証では、サーバ側のアプリケーションもすぐにコネクションを閉じたため、サーバは ACK とあわせて自分の FIN も立て、1つのパケットで送っています。
クライアントが ACK を返す
クライアントは、サーバからの FIN に ACK を返します。これでコネクションが完全に終了します。
1▾ Flags: 0x011 (FIN, ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Nonce: Not set .... 0... .... = Congestion Window Reduced (CWR): Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 0... = Push: Not set .... .... .0.. = Reset: Not set .... .... ..0. = Syn: Not set .... .... ...1 = Fin: Set
2▾ Flags: 0x011 (FIN, ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Nonce: Not set .... 0... .... = Congestion Window Reduced (CWR): Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 0... = Push: Not set .... .... .0.. = Reset: Not set .... .... ..0. = Syn: Not set .... .... ...1 = Fin: Set
3▾ Flags: 0x010 (ACK) 000. .... .... = Reserved: Not set ...0 .... .... = Nonce: Not set .... 0... .... = Congestion Window Reduced (CWR): Not set .... .0.. .... = ECN-Echo: Not set .... ..0. .... = Urgent: Not set .... ...1 .... = Acknowledgment: Set .... .... 0... = Push: Not set .... .... .0.. = Reset: Not set .... .... ..0. = Syn: Not set .... .... ...0 = Fin: Not set
| No. | 表示 | 意味 |
|---|---|---|
| 1 | Flags: 0x011 (FIN, ACK) | クライアントの FIN。ACK も立っている |
| 2 | Flags: 0x011 (FIN, ACK) | サーバの、クライアントの FIN への ACK と、サーバ自身の FIN |
| 3 | Flags: 0x010 (ACK) | サーバの FIN への ACK |
FIN と ACK を別々に送る場合
RFC の基本の形では、サーバは ACK と FIN を別々に送ります。FIN を受け取ったサーバはまず ACK を返し、自分の FIN は、サーバ側のアプリケーションがコネクションを閉じるまで送りません。そのため、アプリケーションの終了処理に時間がかかると、ACK と FIN の間があき、4回のやり取りになります。実際の通信でもよく見られる形です。
- 先に FIN を送った側は、最後の ACK を送ったあと、TIME_WAIT という状態で一定時間(MSL:パケットの最大生存時間の2倍)待ってから、コネクションの情報を消します。最後の ACK が届かず相手が FIN を送り直してきたときに ACK を返し直せるようにするためと、前のコネクションの遅れたパケットが新しいコネクションに混ざらないようにするためです。
- FIN による終了のほかに、RST フラグを立てたパケットで、コネクションを強制的に切ることもあります。
まとめ
| 場面 | やり取り | Wireshark のフラグ |
|---|---|---|
| コネクションの確立 | SYN → SYN/ACK → ACK | 0x002 → 0x012 → 0x010 |
| コネクションの終了(3回) | FIN/ACK → FIN/ACK → ACK | 0x011 → 0x011 → 0x010 |
| コネクションの終了(4回) | FIN/ACK → ACK → FIN/ACK → ACK | 0x011 → 0x010 → 0x011 → 0x010 |
コメント
とても分かりやすかったです、ありがとうございます。
一点、TCPコネクション終了時 の冒頭で
“閉じたい方から”
とありますが、Server 側から能動的に閉じることも可能なのでしょうか。
その場合は、Server -FIN> Client ➡ Client -ACK> Server で完結、でしょうか。
大変お忙しい中お手数で誠に恐縮ながら、教えて頂けますと幸いです。
よろしくお願い申し上げます。