WEB PROTOCOL

TCPコネクション切断(4-Way Close)

FIN → ACK → FIN → ACKの4パケット交換でTCP接続を安全に終了するプロセス

INTERACTIVE VISUALIZATION
FIN
ACK
RST
DATA
プロトコル
TCP
4-Way Close
Client
ESTABLISHED
クライアント状態
Server
ESTABLISHED
サーバー状態
進捗
1 / 8
通信中(ESTABLISHED)
シナリオ
FIN → ACK → FIN → ACKの標準的な4ウェイハンドシェイクで接続を安全に終了します
ステップ1 / 8
自動再生でTCPコネクション切断の流れを順番に確認できます
クライアントとサーバーは双方ともESTABLISHED状態で、データ通信を行っています。
クライアント側から接続の終了を開始します
接続状態
ClientESTABLISHED
接続確立済み
ServerESTABLISHED
接続確立済み
解説

📌
4ウェイクローズとは

4ウェイクローズは、TCP接続を安全に終了するための4パケット交換プロセスです。FIN(終了通知)とACK(確認応答)を双方向でやり取りし、データの取りこぼしなく接続を閉じます。電話を切る場面に例えてみましょう。もし一方的にガチャ切りしたら、相手は「まだ話の途中だったのに」と困りますよね? だからお互いに確認し合ってから電話を切る必要があります。

「もう話すことないです」 (クライアント → FIN送信)
「わかった」 (サーバー → ACK送信)
「こちらも終わりです」 (サーバー → FIN送信)
「了解、バイバイ」 (クライアント → ACK送信)

なぜ4パケットも必要なのでしょうか? TCPは全二重通信(Full-Duplex)、つまりクライアント→サーバー方向とサーバー→クライアント方向の2つの通信路が同時に存在します。各方向を独立して閉じる必要があるため、送信側の終了通知(FIN)と受信側の確認応答(ACK)が2セット、合計4パケットが必要になります。もし2パケットで済ませてしまうと、片方がまだ送りたいデータを持っている場合にデータが失われてしまいます。Webブラウザでタブを閉じたとき、裏側ではこの4パケット交換が行われています。Nginxのようなwebサーバーのアクセスログでは、1秒間に何千回もの4ウェイクローズが実行されています。普段は意識しませんが、インターネット通信の信頼性を支える重要な仕組みです。上のツールで「通常切断(4ウェイ)」シナリオを再生すると、FIN→ACK→FIN→ACKの流れが確認できます。特にクライアントとサーバーの状態(FIN_WAIT_1、CLOSE_WAIT等)がステップごとに変わる点に注目してください。

📌
特徴

  • 🔄双方向独立終了送信側と受信側を個別に閉じるため、片方だけ閉じて残りのデータを受信する「半クローズ(Half-Close)」が可能です。たとえば、クライアントがHTTPリクエストを送り終えた後(FIN送信)、サーバーからの大量のレスポンスデータを引き続き受信できます。データベースのクエリ結果が巨大な場合や、ファイルダウンロード時にこのパターンが使われます。上のツールで「半クローズ」シナリオを選択すると、FIN送信後もサーバーがデータを送り続ける様子が確認できます。
  • TIME_WAIT安全策最後のACK送信後、2MSL(Maximum Segment Lifetime × 2、通常60秒〜120秒)の間待機します。なぜこの待機が必要かというと、最後のACKがネットワーク上で消失した場合、サーバーがFINを再送する必要があるからです。もしクライアントがすぐにCLOSEDになっていたら、サーバーのFIN再送に応答できません。また、古い接続の遅れたパケットが新しい接続に誤って届くことを防ぐ役割もあります。
  • RST即座切断異常時にはRST(Reset)パケット1つで即座に接続を切断できます。4パケット交換もTIME_WAIT待機も不要です。ただし、未送信データは即座に破棄されるため、データロスの可能性があります。プロセスのクラッシュ、存在しないポートへの接続、ファイアウォールによる接続拒否などで発生します。上のツールで「RST強制切断」シナリオを選択すると、わずか1パケットで接続が終了する様子が確認できます。
  • 📊状態遷移管理クライアントはESTABLISHED→FIN_WAIT_1→FIN_WAIT_2→TIME_WAIT→CLOSEDと遷移し、サーバーはESTABLISHED→CLOSE_WAIT→LAST_ACK→CLOSEDと遷移します。各状態で受け付けるパケットが厳密に決まっており、不正なパケットは無視されます。netstat コマンドで netstat -an | grep TIME_WAIT と実行すると、実際にTIME_WAIT状態の接続を確認できます。

📌
ユースケース

🌐 Webサーバー(Nginx)
Webサーバーは1秒間に数千のHTTPリクエストを処理し、Keep-Alive接続のタイムアウト後に4ウェイクローズで接続を終了します。大量の同時接続を適切に閉じることでファイルディスクリプタやメモリを効率的に解放します。Nginxのworker_connections設定が上限に達すると新しい接続を受け付けられなくなるため、適切な接続終了は安定運用の要です。
🗄️ データベース(PostgreSQL)
データベースの接続プール(PgBouncer等)は、一定時間アイドル状態の接続を4ウェイクローズで安全に終了します。トランザクション実行中に突然切断するとデータ不整合が起きるため、FINパケットで「送信完了」を相互に確認してから接続を閉じます。
☁️ CDN(Cloudflare)
CDNのエッジサーバーはオリジンサーバーとの間に大量のTCP接続を持ちます。TIME_WAITにより古い接続の遅延パケットが新しい接続に混入することを防ぎ、コンテンツ配信の信頼性を確保します。Cloudflareは1秒あたり数百万の接続を処理しています。
🐳 Kubernetes
Pod終了時、KubernetesはSIGTERMを送信し、デフォルト30秒のgraceful shutdown期間を設けます。この間にアプリケーションは処理中のリクエストを完了し、全TCP接続を4ウェイクローズで正常終了します。強制切断(SIGKILL)されると進行中のリクエストが失われるため、graceful shutdownの実装が重要です。

📌
用語解説

FIN(Finish)
= 終了通知パケット
「これ以上送るデータはありません」を相手に通知するパケットです。TCPヘッダーのFINフラグビットを1にセットしたセグメントとして送信されます。FINを送信した側はこれ以上データを送信しませんが、相手からのデータ受信は引き続き可能です(半クローズ状態)。FINにもシーケンス番号が割り当てられるため、相手はACKで正確に受信を確認できます。
ClientFINS
ACK(Acknowledge)
= 確認応答
FINに対する「受け取りました」の応答です。TCPのほぼすべてのパケットにACKフラグが含まれますが、4ウェイクローズにおいては、FINに対する「了解しました」の意味で使われます。ACKの確認応答番号(Acknowledgment Number)には、受信したFINのシーケンス番号+1がセットされ、どのFINに対する応答かを明示します。
CACKServer
TIME_WAIT
= 最終待機状態
最後のACK送信後に2MSL(Maximum Segment Lifetime × 2)の間待機する状態です。Linuxでは /proc/sys/net/ipv4/tcp_fin_timeout で値を確認でき、デフォルトは60秒です。大量の短い接続を受けるWebサーバーではTIME_WAIT状態のソケットが蓄積し、ポート枯渇(ephemeral port exhaustion)が問題になることがあります。この場合、SO_REUSEADDR ソケットオプションやカーネルパラメータ net.ipv4.tcp_tw_reuse を使って対策します。
2MSLCLOSED
CLOSE_WAIT
= 受動的クローズ待ち
相手のFINを受信してACKを返したが、自分のFINはまだ送信していない状態です。アプリケーションがclose()システムコールを呼ぶまでこの状態が続きます。アプリケーションがclose()を呼ばずにFIN受信後も接続を放置すると、CLOSE_WAITが蓄積しファイルディスクリプタのリソースリークの原因になります。netstatでCLOSE_WAITが大量に表示される場合、アプリケーションのバグ(コネクションのクローズ漏れ)を疑いましょう。
ServerCLOSE_WAIT
LAST_ACK
= 最後のACK待ち
自分のFINを送信し、相手からの最後のACKを待っている状態です。この状態は通常非常に短時間で、相手のACKが届けばすぐにCLOSEDに遷移します。もしACKが届かない場合、FINの再送タイマーが発動し、一定回数再送した後にタイムアウトで強制的にCLOSEDになります。
ServerLAST_ACKACK?
RST(Reset)
= 強制切断
即座に接続を破棄する強制切断パケットです。TIME_WAITなし、未送信データは即座に消失します。RSTを受信した側は即座にCLOSEDに遷移し、ACKは返しません。デバッグ時に予期しないRSTが見えた場合、ファイアウォールやロードバランサーによるアイドルタイムアウト切断、あるいは存在しないポートへの接続試行の可能性があります。Wiresharkでパケットキャプチャすると、RSTの発生タイミングと原因を特定できます。
ClientRSTS×

📌
切断の手順

TCP接続の切断に必要な4つのステップを追いかけます。

1
FIN: クライアントが終了要求
クライアントがFINパケットを送信し、「もう送るデータはありません」と通知します。この時点でクライアントの状態はESTABLISHEDからFIN_WAIT_1に変わります。クライアントはまだサーバーからのデータを受信可能です(半クローズ状態)。
2
ACK: サーバーが確認
サーバーがACKで応答し、「終了要求を受け取りました」と確認します。クライアントはFIN_WAIT_2に、サーバーはCLOSE_WAITに遷移します。サーバーはまだ送信すべきデータがあれば送信を続けられます。
3
FIN: サーバーも終了通知
サーバーも送信すべきデータがなくなり、FINパケットを送信します。「こちらも終わりです」の通知です。サーバーの状態はLAST_ACKに遷移し、最後のACKを待ちます。
4
ACK: クライアントが最終確認
クライアントがACKを返し、TIME_WAIT状態に入ります。サーバーはこのACKを受信してCLOSEDになります。クライアントは2MSL(約2分)待機した後、CLOSEDに遷移してすべてのリソースが解放されます。
FINACKFINACK4パケットで安全に切断

📌
TIME_WAITとRST

TIME_WAITの理由

1
最後のACKロスへの対応
最後のACKがネットワーク上で消失した場合を考えます。サーバーはACKを受信できないため、FINを再送します。もしクライアントがTIME_WAITなしですぐにCLOSEDになっていたら、再送されたFINに対してRSTを返してしまい、サーバー側で接続エラーが発生します。TIME_WAITがあることで、クライアントはFIN再送に対してACKを再送でき、サーバーも正常にCLOSEDに遷移できます。
2
遅延パケットの混入防止
TCP接続は(送信元IP、送信元ポート、宛先IP、宛先ポート)の4つの組み合わせで識別されます。もし古い接続のクローズ直後に同じ4組で新しい接続を開いた場合、ネットワーク上に残っていた古い接続のパケットが新しい接続に届いてしまう可能性があります。2MSL待機により、古いパケットが確実にネットワークから消滅してから新しい接続を許可します。
ACK送信2MSL 待機CLOSED遅延パケットが消滅するまで待機

通常切断 vs RST強制切断

通常切断(4ウェイ)
  • -4パケット交換(FIN→ACK→FIN→ACK)で双方が合意のもと切断
  • -TIME_WAITあり(2MSL待機で遅延パケットの混入を防止)
  • -データ安全保証(未送信データをすべて送り終えてから切断)
  • -Webサーバーの正常終了やDB接続プールの安全な解放に使用
RST強制切断
  • -1パケット(RSTのみ)で即座に切断。ACK不要
  • -TIME_WAITなし(即座にCLOSEDに遷移しリソース解放)
  • -未送信データは即座に破棄され、データロスの可能性あり
  • -プロセスクラッシュ、存在しないポートへの接続、ファイアウォール拒否で発生
上のツールで「通常切断」と「RST強制切断」の両シナリオを比較すると、パケット数と状態遷移の違いが確認できます。

関連コンテンツ

TCP/IP

TCP/IP

TCPとIPの役割分担、パケット分割から順序通りの組み立てまでの流れを可視化

TCP 3ウェイハンドシェイク

TCP 3ウェイハンドシェイク

SYN・SYN-ACK・ACKの3パケットによる接続確立の流れを可視化

TCPフロー制御

TCPフロー制御

スライディングウィンドウ、スロースタート、AIMDによるTCPの流量制御メカニズムを可視化

HTTP通信

HTTP通信

ブラウザとサーバー間のHTTPリクエスト/レスポンスの仕組みを可視化

TLSハンドシェイク

TLSハンドシェイク

HTTPSで安全に通信するためのTLSハンドシェイクの仕組みを可視化

DNS名前解決

DNS名前解決

ドメイン名からIPアドレスへの階層的な問い合わせの流れを可視化