【トラブルシューティング】TCP状態遷移の解説

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 の接続のトラブルシューティングでは、この状態の意味を理解しておく必要がある場面があります。この記事では、接続の確立から終了までの状態の移り変わりを、ひとつずつ見ていきます。

この記事でわかること
  1. 3ウェイ ハンドシェイクで接続を確立するまでの状態(LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED)
  2. 接続を閉じるまでの状態(FIN-WAIT-1、CLOSE-WAIT、FIN-WAIT-2、LAST-ACK、TIME-WAIT)
  3. netstat で見える状態を、トラブルシューティングでどう読むか

全体の流れ

クライアントからサーバーに接続し、クライアント側から接続を閉じる場合の流れは、次のとおりです。

クライアントサーバー LISTEN 待ち受け SYN SYN-SENT SYN を送って応答待ち SYN+ACK SYN-RECEIVED SYN+ACK を送って ACK 待ち ACK ESTABLISHED 接続確立 ESTABLISHED 接続確立 データ送受信 FIN FIN-WAIT-1 FIN を送って ACK 待ち ACK CLOSE-WAIT アプリの終了待ち FIN-WAIT-2 相手の FIN 待ち FIN LAST-ACK 最後の ACK 待ち ACK TIME-WAIT CLOSED CLOSED 2MSL 待つ(4 分)
図1 TCP の状態遷移の全体の流れ

SYN-SENT 状態

サーバーは、接続を待ち受ける LISTEN 状態にあるとします。クライアントの TCP は、アプリケーション(ソケット)から接続を求められると、サーバーに SYN を送り、SYN-SENT 状態になります。

クライアントサーバー LISTEN 待ち受け SYN SYN-SENT SYN を送って応答待ち
図2 SYN を送って SYN-SENT になる

SYN-RECEIVED 状態

サーバーはクライアントの SYN を受け取ると、SYN+ACK をクライアントに送り返し、SYN-RECEIVED 状態になります(netstat では SYN_RECV などと表示されます)。

クライアントサーバー LISTEN 待ち受け SYN SYN-SENT SYN を送って応答待ち SYN+ACK SYN-RECEIVED SYN+ACK を送って ACK 待ち
図3 SYN+ACK を返して SYN-RECEIVED になる

この、最後の ACK を待っている途中の接続は、SYN フラッド攻撃の説明などでハーフオープン(half-open)の接続と呼ばれることがあります。

ESTABLISHED 状態

クライアントは、サーバーから SYN+ACK を受け取って ACK を送り返すと、ESTABLISHED 状態になります。サーバーも、クライアントからの ACK を受け取ると ESTABLISHED 状態になります。これで、双方向にデータを送受信する準備ができました。この SYN、SYN+ACK、ACK のやり取りを、3ウェイ ハンドシェイクと呼びます。

クライアントサーバー LISTEN 待ち受け SYN SYN-SENT SYN を送って応答待ち SYN+ACK SYN-RECEIVED SYN+ACK を送って ACK 待ち ACK ESTABLISHED 接続確立 ESTABLISHED 接続確立
図4 ACK を受け取って ESTABLISHED になる

データの送受信

ESTABLISHED の状態で、クライアントとサーバーの間でデータを送受信します。

クライアントサーバー データ送受信 クライアント サーバー
図5 ESTABLISHED の状態でデータを送受信する

FIN-WAIT-1 状態

クライアント側で送るデータがなくなると、クライアントはサーバーに FIN を送り、FIN-WAIT-1 状態になります。

クライアントサーバー FIN FIN-WAIT-1 FIN を送って ACK 待ち クライアント サーバー
図6 FIN を送って FIN-WAIT-1 になる

CLOSE-WAIT 状態

サーバーはクライアントからの FIN を受け取ると、クライアントに ACK を送り、CLOSE-WAIT 状態になります。この状態では、クライアントからサーバーへの方向は閉じていますが、サーバーからクライアントへはまだデータを送れます。このような状態を、ハーフクローズと呼ぶこともあります。

クライアントサーバー ACK CLOSE-WAIT アプリの終了待ち クライアント サーバー
図7 ACK を返して CLOSE-WAIT になる

CLOSE-WAIT は、サーバー側のアプリケーションが接続を閉じる(close する)のを待っている状態です。

FIN-WAIT-2 状態

クライアントは、サーバーから ACK を受け取ると FIN-WAIT-2 状態になり、サーバーから FIN が届くのを待ちます。

クライアントサーバー ACK CLOSE-WAIT アプリの終了待ち FIN-WAIT-2 相手の FIN 待ち クライアント サーバー
図8 ACK を受け取って FIN-WAIT-2 になる

LAST-ACK 状態

サーバーのアプリケーションが接続を閉じると、サーバーは FIN を送り、LAST-ACK 状態になります。この FIN に対する ACK を受け取るまでこの状態で待ち、ACK を受け取ると CLOSED 状態になります。

クライアントサーバー FIN LAST-ACK 最後の ACK 待ち ACK TIME-WAIT CLOSED クライアント サーバー
図9 FIN を送って LAST-ACK になり、ACK で CLOSED になる

TIME-WAIT 状態

クライアントは、サーバーから FIN を受け取って ACK を送ると、TIME-WAIT 状態になります。TCP の仕様では MSL(Maximum Segment Lifetime:最大セグメント生存時間)を 2 分とし、TIME-WAIT では MSL の 2 倍の時間待つと決めています。待ち時間が過ぎると、CLOSED 状態になります。

クライアントサーバー FIN LAST-ACK 最後の ACK 待ち ACK TIME-WAIT CLOSED CLOSED 2MSL 待つ(4 分) クライアント サーバー
図10 ACK を返して TIME-WAIT になり、2MSL 待って CLOSED になる

TIME-WAIT で待つのは、最後に送った ACK が相手に届いたことを確かめるためと、ネットワークに残っている古いセグメントが、同じアドレスとポートを使う新しい接続に紛れ込まないようにするためです。

参考までに、Windows では TIME-WAIT の既定の待ち時間は 4 分で、RFC の 2MSL と同じです(TCP/IP ポート不足のトラブルシューティング – Windows Client)。

RFC 793 は RFC 9293 に置き換わっている

TCP の仕様は、2022年に RFC 793 から RFC 9293 に置き換わりました。状態の名前や、MSL を 2 分として TIME-WAIT で 2MSL 待つという内容は、RFC 9293 でも同じです。

トラブルシューティングでの見方

netstat でたくさん見えている状態や、長く残っている状態から、次のようなことがわかります。

状態考えられること
SYN_SENTSYN を送ったが応答がない。相手に届いていない、途中で破棄されている、相手が応答していない、などが考えられる
SYN_RECVSYN+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両方接続がない

コメント