現場のエンジニアに捧ぐ:TCPの「終了」を見極める――FINとRSTの境界線
ネットワークエンジニアとして夜通しトラブルシューティングをしていると、パケットキャプチャの画面が真っ黒に見えてくる瞬間があります。そんな時、一番厄介なのが「なぜか繋がらない」「なぜか切れる」という曖昧な挙動です。
Web APIの設計やインフラ運用において、TCPの終了シーケンスは単なる「おまけ」ではありません。ここを理解していないと、ロードバランサーのアイドルタイムアウトで泣きを見ることになります。今日は、綺麗に幕を引くFINと、問答無用にドアを蹴破るRSTの話をしましょう。
—
1. 礼儀正しい別れ:FINによる正常終了(4ウェイ・ハンドシェイク)
TCP接続の終了は、通信の両端が「もう送るデータはないよ」と合意するプロセスです。これを「4ウェイ・ハンドシェイク」と呼びます。
1. FIN送信: 終了したい側(Active Close側)がFINフラグを立てます。
2. ACK応答: 受け取った側はACKを返し、自分も送るものがないか確認します。
3. FIN送信: 準備ができたら、もう片側もFINを送ります。
4. ACK応答: 最後にACKを返してクローズ完了です。
このプロセスが正しく行われると、SocketはTIME_WAIT状態に入ります。これは「まだネットワーク上に迷子のパケットがいるかもしれないから、しばらく同じポートの再利用を待つね」という、ネットワークの安全を守るための賢い猶予期間です。
—
2. 暴力的な強制切断:RSTフラグの正体
現場で最も嫌われるのが、このRST(Reset)フラグです。これは「会話の途中だけど、もうお前とはやってられない!」という通信の強制破棄を意味します。
なぜRSTが飛ぶのか?
多くの場合、以下のようなケースで発生します。
- ポートが閉まっている: サーバー側で該当ポートが待ち受け状態ではない。
- 異常なパケット到着: 確立されていないセッションに対するパケットが届いた。
- タイムアウト: ロードバランサーが「こいつ、長居しすぎだ」と判断してセッションをブチ切る。
- バッファオーバーフロー: 処理しきれないデータが届き、通信を継続不能と判断する。
特に、Web API開発で「Connection reset by peer」というエラーに出くわしたら、それはほぼ間違いなく通信経路上やサーバー側でRSTが発行されています。
—
3. 実践:デバッグと検証の現場から
実務で遭遇する「謎の切断」を切り分けるためのTipsを紹介します。
curlでFINとRSTを観察する
curlを使って、特定のサーバーへの接続を観察してみましょう。-v(verbose)オプションは必須です。
# -v でヘッダーのやり取りと接続状況を確認する
curl -v https://api.example.com
もしここで Recv failure: Connection reset by peer と表示されたら、サーバー側のファイアウォール(iptables/nftables)や、手前のWAFが接続を拒否している可能性が高いです。
PythonでRSTを「意図的に」発生させる
実験として、受信バッファを無視して即座にクローズすることでRSTを誘発する例です。
import socket
# ソケットを作成
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 接続
s.connect(('127.0.0.1', 80))
# SO_LINGERオプションでタイムアウトを0に設定して強制終了を誘発
# これにより、FINではなくRSTが投げられる
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, b'\x01\x00\x00\x00\x00\x00\x00\x00')
s.close()
Nginxでの設定ポイント
インフラ運用でよくある「意図しない切断」を防ぐには、keepalive_timeoutの調整が肝です。バックエンドのAPIサーバーが処理に時間がかかる場合、Nginx側で早く切りすぎるとクライアントにRSTが届きます。
# nginx.conf の設定例
http {
# クライアントとの接続を維持する時間。短すぎると頻繁な切断を招く
keepalive_timeout 65s;
# 接続がタイムアウトした際に、即座にRSTを投げるかどうかの制御
# reset_timedout_connection on;
}
—
4. 最後に:パケットは嘘をつかない
トラブルシューティングの極意は「ログを信じるな、パケットを見ろ」です。アプリケーションのログには「接続エラー」としか出なくても、tcpdumpやWiresharkでRSTフラグの有無を確認すれば、それが「通信の行き止まり」なのか「強制排除」なのかが一目瞭然になります。
エンジニアとして、このTCPの「作法」を知っているかどうかで、障害復旧のスピードは劇的に変わります。次にAPIが切断された時は、ぜひパケットの中身にフラグを立ててみてください。きっと、ネットワークの向こう側の意図が、少しだけ見えるはずです。
それでは、また次回の深掘り記事でお会いしましょう。現場からは以上です。
コメント