socket の TIME_WAIT

socket 通信をしてて、クライアントから一度接続を切って、その後すぐ再接続したらエラーになりました。。。
環境はこんな感じ。
サーバー: 192.168.1.1:52201 を listen
クライアント:192.168.1.2:52202 から connect
エラー内容は下記。

接続失敗
WSAEADDRINUSE (10048)
Address already in use
アドレスは既に使用中である。
・ bind()しようとしたアドレスは、既にほかのソケットで使われている。
同じローカルアドレスを複数のソケットで使うためには、
SO_REUSEADDRソケットオプションをTRUEにする必要がある。

netstat してみたら、たしかにクライアント側に TCP セッションが TIME_WAIT 状態で残ってました。

netstat -na

アクティブな接続

  プロトコル  ローカル アドレス          外部アドレス        状態
  ...(snip)...
  TCP    192.168.1.2:52202    192.168.1.1:52201    TIME_WAIT

TIME_WAIT なんで、放っておけば勝手に消えてくれて、再接続可能になるはず。
実際今回のケースでも、上記 TIME_WAIT のエントリが消えた後に試したら、繋がりました。


ここで「はて、いままでにもクライアントで socket の open/close を繰り返したことなんて何度もあったけど、TIME_WAIT になんてならなかったぞ?」
と不思議に思いましたが、送信ポートを明示的に指定しているのが原因なんですね。
つまりは、送信側で bind() してるから悪い。
特に送信ポートに拘る必要は無かったので、空いてるポートからガシガシ使っていくように変更したら、解消されました。
# なんてこたぁーない、bind() 呼ばずに connect() するってだけの話


たしかに、サーバアプリを一度落として再起動すると、同じ現象で起動失敗したりしますね。
あれも、サーバ側で bind() してた TCP コネクションが残ってるから。


で、興味あったのでちょっと調べてみた感じだと、
socket を close した側で TIME_WAIT が発生して、
close された側では TIME_WAIT 発生しないみたいですね。
close した側は、相手側(close された側)からさらに何か送信されてくるかもしれないから、
それに備えてから消える、ってイメージでしょうか。