ソケット通信で切断後,しばらく再接続できないのです

解決


NewCer  2003-11-27 02:55:13  No: 52622  IP: [192.*.*.*]

こんにちは

socket通信プログラムのことでお尋ねしたいのですが,クライアントソフトを作っています。
通信相手のサーバはサーバ側のポートだけでなく,接続してきたクライアントのIPとポートをチェックするためクライアント側でも bind()を使ってポートを指定してプログラムを作成しました。
一回目は接続できるのですが,クライアントから通信を切断して再度続しようとすると接続できません,なぜかと思い netstat -na で見ると該当するコネクションが残っており TIME_WAITが消えるまでは再接続ができないことが判りました。
切断後も直ちに再接続ができるようにするためにはどうすればよいのでしょうか?
TIME_WAITを出さないようにする方法などがあるのでしょうか?
よろしくお願いします。

Windows2000pro VC++6.0 SDKの環境です。
C とWin32APIで作成しています。

編集    削除
瀬戸っぷ  2003-11-27 03:02:04  No: 52623  IP: [192.*.*.*]

> 一回目は接続できるのですが,クライアントから通信を切断して再度続しようとすると接続できません,
> なぜかと思い netstat -na で見ると該当するコネクションが残っており
>  TIME_WAITが消えるまでは再接続ができないことが判りました。
> 切断後も直ちに再接続ができるようにするためにはどうすればよいのでしょうか?

netstat -naを実行したのがクライアント側なのかサーバ側なのか不明ですが…
切断した時にshutdown()は実行していますか?
あとは…closesocket()とか……

WinSock関係だと、下記のサイトなどが参考になるかと。
http://www.nakka.com/lib/inet/index.html
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/

編集    削除
AUT`s  2003-11-27 03:59:21  No: 52624  IP: [192.*.*.*]

不可能。コネクションをクローズするステップがあるに。最大で2MSL(大体60秒前後の設定)の遅延。
まぁ、TIME_WAITもなしに連続的に接続すると攻撃とみなされるからやめときなさ。

切断時にお行儀の良く終了すれば、多少は軽減できるかも。
shutdown(SD_SEND)→recv()をまわして全部受信→shutdown(SD_RECEIVE もしくは SD_BOTH)→closesocket()

しかし、文章だけみてるとあまりヨロシクないプログラムを書こうとしてるな。
他人(この場合はネットを使ってる全員)に迷惑だけはかけんように。

編集    削除
NewCer  2003-11-27 04:34:31  No: 52625  IP: [192.*.*.*]

瀬戸っぷさん
 NewCerです,情報をありがとうございます。

>netstat -naを実行したのがクライアント側なのかサーバ側なのか不明ですが…
クライアント側での状態です。

>切断した時にshutdown()は実行していますか?
>あとは…closesocket()とか……

これまで,切断するときには以下のようにしていましたが
  closesocket(sock);
  sock = INVALID_SOCKET;

shutdown()を追加し以下のようにしてみました。

 shutdown(sock,SD_BOTH); // 追加
 closesocket(sock);
 sock = INVALID_SOCKET;

が,結果は変化がありませんでした (涙

以下のサイトも参考にさせていただきました。rim.or.jpのサイトは今回のプログラムを作るのにも参考にさせてもらっていました。nakka.comのサイトは非常にわかりやすい説明で即,ブックマークさせていただきました。m(__)m

http://www.nakka.com/lib/inet/index.html
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/

ただ,クライアント側でポートを指定したプログラムがどこにも紹介されておらず,今の方法に何か足りないコードがあるような気がしてなりません。
わたしが書いたコードは上記のサイトでも紹介されている単純なクライアント接続コードに下記のように単に bind()を挿入しただけなのです。
socket()
bind()
gethostbyname()
WSAAsyncSelect()
connect()

やはり,SetTimer()などをつかって TIME_WAITを短くするしか手がないのでしょうか・・・

編集    削除
NewCer  2003-11-27 04:44:17  No: 52626  IP: [192.*.*.*]

AUT`sさん
 こんにちは,情報をありがとうございます。

>不可能。コネクションをクローズするステップがあるに。最大で2MSL(大体60秒前後の設定)の遅延。
>まぁ、TIME_WAITもなしに連続的に接続すると攻撃とみなされるからやめときなさ。

>切断時にお行儀の良く終了すれば、多少は軽減できるかも。
>shutdown(SD_SEND)→recv()をまわして全部受信→shutdown(SD_RECEIVE もしくは SD_BOTH)→closesocket()

参考にさせていただきます。

>しかし、文章だけみてるとあまりヨロシクないプログラムを書こうとしてるな。
>他人(この場合はネットを使ってる全員)に迷惑だけはかけんように。

確かに,ネットワークプログラムは閉じた世界でないだけに下手なプログラムは迷惑になりますね,ただ今作っているのは工場の中の閉じたLANの中での話です。工場を爆発させないように気をつけます(爆)
TIME_WAITに関しては 例えばメールを受信すると popサーバと通信しますが切断後も TIME_WAITが残っています。しかしその状態でメールソフトで再度受信を実行してもちゃんと接続し受信動作もしてくれるので同じようにできないのかなぁと・・・技術の差と言えばそれまでなのですが,そのあたりを調べましたが万策尽きてここに質問させていただいたと言うわけです。
もちろん,メールサーバの接続はクライアントのポートは問われないのでこのあたりが何かあるのかなぁ〜ぐらいしかわかりません

編集    削除
NewCer  2003-11-27 05:54:56  No: 52627  IP: [192.*.*.*]

試行錯誤しているうちに以下のことがわかりました。

サーバの設定をゆるくして,クライアントのポートをチェックしないようにしてクライアントも自ポートを指定しない方法で接続すると,一つの接続を終了した時点で TIME_WAITは出ますが,再接続を試みると接続できました。
netstat で見ると前の接続は TIME_WAITですが,クライアントは最初とは別のポート番号でサーバと接続していました。
どうもクライアント側のポートが使えないと言うのが問題のようです。
こうなるとやはり TIME_WAITの時間をデフォルトより短くするようなコーディングが必要かなと思い始めています。

編集    削除
AUT`s  2003-11-27 06:28:53  No: 52628  IP: [192.*.*.*]

> デフォルトより短くするようなコーディングが必要
いや、だからsocket実装にはTIME_WAITが必ず入るから。ちなみに、オプションで短くするような設定はできませぬ。
どんなに良いコーディングをしようが、socketを使う限りはTIME_WAITから逃げられんよ。
そういえば、connect時にタイムアウトを短くさせる論議があった時、TIME_WAITについて触れてたっけ。
MSLの定義が分かれているのも、その辺りで問題が生じたから。基本的に切断処理にリソースを喰うのよネ〜。

> どうもクライアント側のポートが使えないと言うのが問題のようです。
最初ッからその問題やん。しかし、なんでクライアントのportを見る必要があるのかな?
根本的にそこが不明(w。
普通はクライアントのport番号なんか見ないから、次々とportを開いて接続できるのに。
絶対的に1コネクションのみと制限する為にそう設定されているなら、安易というか、間違いというか(w。

ちなみに通常のメールソフトなんかは、クライアント側のport番号なんか見られてないから、適当に空いているportを次々と使用していくに。
ってか、bind()呼ばずにconnect()すれば勝手にそうなる。

> サーバの設定をゆるくして
クライアントの使用portをチェックする方法は基本的に無意味なり。
工場で使う用途(例えばハード制御でTCPスタックを使用するなど)にもよるけど、基本的にここの設定が間違いだと思うぞ〜

編集    削除
AUT`s  2003-11-27 06:33:15  No: 52629  IP: [192.*.*.*]

ゴメソ。誤字訂正。

> ちなみに通常のメールソフト
ちなみに通常の「メールサーバー」の設定は

そうそう、それとTIME_WAITを短くする方法。
・LANの帯域を大きくする。
・分散サーバーを使用する。
・サーバーの負荷を常に一定以下に保つ。
などなど。

編集    削除
AUT`s  2003-11-27 06:37:54  No: 52630  IP: [192.*.*.*]

あああ、まただ...落ち着け俺。

> TIME_WAITを短くする方法。
TIME_WAIT自体を短くするのではなくて、TIME_WAITで待機している時間を短くする方法。
サーバーから応答さえくれば待機状態からCLOSE状態へ遷移するので、結果的に短くなる。

編集    削除
NewCer  2003-11-27 06:59:31  No: 52631  IP: [192.*.*.*]

AUT`sさん

NewCerです,いろいろありがとう御座います。

結論は 「TIME_WAITからは逃れられない」のようですね
クライアントのポートまで見るのは一つのモードでして,ゆるいモードは通常のサーバと同じくサーバ側のポートだけを合わせればよいのですが,厳しいモードですと,それに加えてクライアントのIP,ポート,MACまで見るようになっています。
わたし的には MACさえ見れば十分じゃないのと言いたいのですが・・・

>サーバーから応答さえくれば待機状態からCLOSE状態へ遷移するので、結果的に短くなる。

パケットモニターで切断時の遷移を見てみると以下のように礼儀正しく終わっているように思えるのですが,3分くらい TIME_WAITが消えません

Client -> FIN, ACK -> Server
Client <-   ACK    <- Server
Client <- FIN, ACK <- Server
Client ->   ACK    -> Server

TCP/IPは奥が深い・・・

編集    削除
NewCer  2003-11-27 09:20:30  No: 52632  IP: [192.*.*.*]

一応解決にします。

編集    削除
Miura  2021-06-11 13:46:31  No: 149707  IP: [192.*.*.*]

クローズするときに
WSACleanup();
を実行しておくとリソースが解放され直ぐに再接続できるようになると思う。

編集    削除