TCP は、信頼性のあるコネクション型のプロトコルです。データを送る前にコネクションを確立し、データを送り終わったらコネクションを閉じます。その間、TCP の接続はいくつかの「状態(state)」を順に移っていきます。
たとえば netstat コマンドを使うと、それぞれの TCP 接続がいまどの状態にあるかがわかります。
netstatの出力例 Active Internet connections Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp4 0 0 192.168.3.5.53254 server-143-204-8.https SYN_SENT tcp4 0 517 192.168.3.5.53253 server-143-204-8.https ESTABLISHED tcp4 0 596 192.168.3.5.53252 nrt20s20-in-f2.1.https ESTABLISHED tcp4 0 0 192.168.3.5.53189 23.96.112.38.https FIN_WAIT_1 tcp4 0 517 192.168.3.5.53250 182.22.24.124.https ESTABLISHED tcp4 31 0 192.168.3.5.52276 162.125.80.17.https CLOSE_WAIT tcp4 0 0 192.168.3.5.53195 23.96.112.38.https TIME_WAIT
TCP の接続のトラブルシューティングでは、この状態の意味を理解しておく必要がある場面があります。この記事では、接続の確立から終了までの状態の移り変わりを、ひとつずつ見ていきます。
- 3ウェイ ハンドシェイクで接続を確立するまでの状態(LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED)
- 接続を閉じるまでの状態(FIN-WAIT-1、CLOSE-WAIT、FIN-WAIT-2、LAST-ACK、TIME-WAIT)
- netstat で見える状態を、トラブルシューティングでどう読むか
全体の流れ
クライアントからサーバーに接続し、クライアント側から接続を閉じる場合の流れは、次のとおりです。
SYN-SENT 状態
サーバーは、接続を待ち受ける LISTEN 状態にあるとします。クライアントの TCP は、アプリケーション(ソケット)から接続を求められると、サーバーに SYN を送り、SYN-SENT 状態になります。
SYN-RECEIVED 状態
サーバーはクライアントの SYN を受け取ると、SYN+ACK をクライアントに送り返し、SYN-RECEIVED 状態になります(netstat では SYN_RECV などと表示されます)。
この、最後の ACK を待っている途中の接続は、SYN フラッド攻撃の説明などでハーフオープン(half-open)の接続と呼ばれることがあります。
ESTABLISHED 状態
クライアントは、サーバーから SYN+ACK を受け取って ACK を送り返すと、ESTABLISHED 状態になります。サーバーも、クライアントからの ACK を受け取ると ESTABLISHED 状態になります。これで、双方向にデータを送受信する準備ができました。この SYN、SYN+ACK、ACK のやり取りを、3ウェイ ハンドシェイクと呼びます。
データの送受信
ESTABLISHED の状態で、クライアントとサーバーの間でデータを送受信します。
FIN-WAIT-1 状態
クライアント側で送るデータがなくなると、クライアントはサーバーに FIN を送り、FIN-WAIT-1 状態になります。
CLOSE-WAIT 状態
サーバーはクライアントからの FIN を受け取ると、クライアントに ACK を送り、CLOSE-WAIT 状態になります。この状態では、クライアントからサーバーへの方向は閉じていますが、サーバーからクライアントへはまだデータを送れます。このような状態を、ハーフクローズと呼ぶこともあります。
CLOSE-WAIT は、サーバー側のアプリケーションが接続を閉じる(close する)のを待っている状態です。
FIN-WAIT-2 状態
クライアントは、サーバーから ACK を受け取ると FIN-WAIT-2 状態になり、サーバーから FIN が届くのを待ちます。
LAST-ACK 状態
サーバーのアプリケーションが接続を閉じると、サーバーは FIN を送り、LAST-ACK 状態になります。この FIN に対する ACK を受け取るまでこの状態で待ち、ACK を受け取ると CLOSED 状態になります。
TIME-WAIT 状態
クライアントは、サーバーから FIN を受け取って ACK を送ると、TIME-WAIT 状態になります。TCP の仕様では MSL(Maximum Segment Lifetime:最大セグメント生存時間)を 2 分とし、TIME-WAIT では MSL の 2 倍の時間待つと決めています。待ち時間が過ぎると、CLOSED 状態になります。
TIME-WAIT で待つのは、最後に送った ACK が相手に届いたことを確かめるためと、ネットワークに残っている古いセグメントが、同じアドレスとポートを使う新しい接続に紛れ込まないようにするためです。
参考までに、Windows では TIME-WAIT の既定の待ち時間は 4 分で、RFC の 2MSL と同じです(TCP/IP ポート不足のトラブルシューティング – Windows Client)。
TCP の仕様は、2022年に RFC 793 から RFC 9293 に置き換わりました。状態の名前や、MSL を 2 分として TIME-WAIT で 2MSL 待つという内容は、RFC 9293 でも同じです。
トラブルシューティングでの見方
netstat でたくさん見えている状態や、長く残っている状態から、次のようなことがわかります。
| 状態 | 考えられること |
|---|---|
| SYN_SENT | SYN を送ったが応答がない。相手に届いていない、途中で破棄されている、相手が応答していない、などが考えられる |
| SYN_RECV | SYN+ACK を返したが、最後の ACK が来ない。大量にある場合は、SYN フラッド攻撃なども疑う |
| CLOSE_WAIT | 相手から FIN を受け取ったが、自分のアプリケーションが接続を閉じていない。たまり続ける場合は、アプリケーションが close していない可能性がある |
| TIME_WAIT | 自分から閉じた接続が待っている。正常な状態だが、短い接続を大量に作っていると、ポートが足りなくなる原因になることがある |
冒頭の netstat の例でも、CLOSE_WAIT の行の Recv-Q が 31 になっています。Recv-Q は、受信したもののアプリケーションがまだ読み取っていないデータのバイト数です。
まとめ
| 状態 | どちら | 意味 |
|---|---|---|
| LISTEN | サーバー | 接続を待ち受けている |
| SYN-SENT | クライアント | SYN を送って、SYN+ACK を待っている |
| SYN-RECEIVED | サーバー | SYN+ACK を送って、ACK を待っている |
| ESTABLISHED | 両方 | 接続が確立し、データを送受信できる |
| FIN-WAIT-1 | クライアント | FIN を送って、ACK を待っている |
| CLOSE-WAIT | サーバー | FIN を受け取って ACK を返し、アプリケーションが閉じるのを待っている |
| FIN-WAIT-2 | クライアント | ACK を受け取り、相手の FIN を待っている |
| LAST-ACK | サーバー | FIN を送って、最後の ACK を待っている |
| TIME-WAIT | クライアント | 最後の ACK を送ったあと、2MSL 待っている |
| CLOSED | 両方 | 接続がない |
コメント