ASyncSocketで、サーバー側からクローズした時はすぐに再接続できるのですが、
クライアント側からクローズすると再接続に4分近くかかってしまいます。
コネクションをクローズした後、同じポートですぐに再接続するには
どうすれば良いのでしょうか?
また、どうして再接続に4分近くかかってしまうのでしょう?
ご存知の方、教えて下さい。
サーバーのリスニングは同じポートでも、
クライアントは再接続するときに空きポートを使用するんじゃないでしょうか?
私は、CAsyncSocketクラスをよく知らないので、間違ってたらごめんなさい。
参考:(API)
ソケットオプションを設定すれば、使用済みポートをバインド出来ます。
const int on=1;
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
確かにポート指定をせずにソケットを生成すると
Windowsが自動的にポートを設定してくれます。
m_ConnectSock.Create(); //ソケット生成(生成ポート指定なし)
m_ConnectSock.Connect(m_ServerAdd, m_ServerPort); //コネクション確立要求
ですが、サーバーがメッセージを投げるポート(クライアントは受け取るポート)
が固定値になっているためコネクションを再接続する時、別のポートで生成すると
メッセージを受け取れなくなってしまうのです。
m_ConnectSock.Create(m_ClientPortNo); //ソケット生成(生成ポート指定あり)
m_ConnectSock.Connect(m_ServerAdd, m_ServerPort); //コネクション確立要求
初めて書込みするので使用上の注意を読もうと思ったら表示されなかった(泣)
過去ログを582ページ中277ページまで読みましたがとても参考になります。(苦)
私はまだまだVC++を始めて間もないですが...(間違いなく初心者です)
先日まで同じような内容で非常に困っていました。全てが解決したわけでは
無いのですがご参考にでもなれば...
>ASyncSocketで、サーバー側からクローズした時はすぐに再接続できるのですが、
>クライアント側からクローズすると再接続に4分近くかかってしまいます
接続されるんですか?知らなかった...気が短いのか接続されないと思っていました。
で、私はサーバ側とクライアント側を同じソースで記述しているので見難いかも知れませんが...
if(m_R_Server == 0)
{
/* ***
* サーバ側処理
* サーバの場合、設定されたポート番号でソケットを作成し解放
* 一つだけ受信ソケットを作成する
*** */
/* ***
* ソケットポートのクリエイト サーバは常時[0]番の配列でListen
*** */
c_ret = m_ListenSock[0].Create(m_PortNum);
/* ***
* クライアントからの接続待ち どのクライアントからもこのコントロールで受信
*** */
m_ListenSock[0].Listen();
}
else
{
/* ***
* クライアント側処理
* クライアントのソケット作成・接続(検索)処理 クライアントはどこも[0]番の配列でconnect
*** */
// ソケットのクリエイト
m_ConnectSock[0].Create();
// コネクションの検索
m_ConnectSock[0].Connect(m_ServerName, m_PortNum);
}
void C****Dlg::OnAccept()
{
/* ***
* サーバ側処理(クライアント側からの接続要求時にcallされる)
* ソケットクラスのイベント関数定義による追加 (ソケット接続要求を通知するためにcallされる関数)
* ソケット接続要求は常時Listen[0]で受信する
* 接続要求に対してconnectコントロールが空いていればコントロールをacceptする
*** */
short i = 0;
for(i=0;i<ENT_MAX;i++) // ENT_MAX:クライアントの上限
{
if((i == (ENT_MAX-2)) && sock_flg[ENT_MAX-2] == 1)
{
/* ***
* ソケットコントロールが一杯なのにクライアントから接続要求があった場合のサーバ側エラー表示
*** */
MessageBox(定員オーバー!(サーバ), エラー, MB_ICONWARNING | MB_OK);
}
else if(sock_flg[i] == 0)
{
m_ListenSock[0].Accept(m_ConnectSock[i+1]);
sock_flg[i] = 1;
break;
}
}
}
です。
サーバ側は配列[0]で常時listenしています。接続要求があればコントロールを
配列[1]で実行させます。(次の接続要求は[2])
サーバ側が接続を認識するためにsock_flg[ENT_MAX]を使用しています。
もちろん以下の設定も必要です。
for(i=0;i<ENT_MAX;i++)
{
m_ConnectSock[i].SetpWnd(this);
}
クライアント側は1つしか実行されないので常時配列[0]を使用しました。
自分でやっているだけなので詳しい事は解りませんが、createやacceptやlistenは
同じコントロールで何回もcallしてはいけないのではないでしょうか?
(クライアントからの要求でcreateやlistenが再callされていませんか?)
デバッグ上ではエラーが帰ってきているはずですが...
こんなので参考になりますか?
書込み素人なので許してください。
ついでにこのレスをお借りして...
使用上の注意が読めないのは私だけですか?
TIME_WAITの話ですな
TCPの3WAYハンドシェークにかかわる話で
切断要求した側が
最後のackを送ることになり
それは、相手に届いても、相手は切断完了しているので
結局遅れたかどうかわからない(TCPの仕様)
なんで、パケットがルータなんかで再送されることも
考慮して、そのackがネットワーク上から消えるまでの時間
そのポートをTIME_WAIT状態にする
というものです
1.回避方法は自分から切断しない
2.SO_LINGERを設定する
SO_LINGERの話は面倒なんで調べてください(極々微小の問題もあり)
TIME WAITであればSO_REUSEADDRで解決すると思います。
>切断要求した側が
>最後のackを送ることになり
>それは、相手に届いても、相手は切断完了しているので
>結局遅れたかどうかわからない(TCPの仕様)
SO_LINGERを設定すると、
この最後のACKが相手側に届かない時に、相手側からの再送FINに、
正しく応答出来なくなります。
ごめん、カナリ激しく間違った・・・
はむさん、愛渚さん、bosscatさん、ありがとうございます。
おそらくTIME_WAITだと思うので、とりあえずやってみます。
MSDNライブラリでは「使用中のローカルアドレスにソケットをバインドすることはできません」
とあります。これは、「あるソケットが接続(バインド?)状態にある時にそのソケットが使っている
ポートを使って別のソケットが接続(バインド?)できない」と言う考え方で良いのでしょうか?
SO_REUSEADDRはSetSockOptで設定するのですが、どのように使えばいいのですか?
MSDNライブラリ(SetSockOpt項)に「このオプションはBindを呼び出す時のみ解釈されます」とあります。
つまり、
m_ConnectSock1.Create(m_ClientPort); // ソケット生成(生成ポート指定あり)
m_ConnectSock1.Connect(m_ServerAdd, m_ServerPort); // コネクション確立要求
// ↑ここまでで m_ServerPort,m_ClientPort は使用中になりますよね
const BOOL Opt = 1;
m_ConnectSock2.Create(m_ClientPort); // ソケット生成(生成ポート指定あり)
m_ConnectSock2.SetSockOpt(SO_REUSEADDR,&Opt,sizeof(Opt),SOL_SOCKET); // SO_REUSEADDR 設定
m_ConnectSock2.Bind(m_ServerPort,m_IPAddress); // バインド呼び出しで解釈?
m_ConnectSock1.Close();
m_ConnectSock2.Connect(m_ServerAdd, m_ServerPort); // コネクション確立要求(ソケット再利用?)
という使い方で良いのでしょうか?
m_ConnectSock.Create(m_ClientPort); // ソケット生成(生成ポート指定あり)
m_ConnectSock.Connect(m_ServerAdd, m_ServerPort); // コネクション確立要求
のようにBindを使わなくてもコネクションを確立できますが、
Bindってどういう働きをしているのですか?
ごちゃごちゃしてきたのでまとめますと、
①Bindの働きと必要性は?
②SO_REUSEADDRの使い方は?
基本的な質問で申し訳ありませんが教えて下さい。
愛渚さんへ、私も使用上の注意が読めませんでした。
ありがとうございました
今回は4分を気にしないでやってみて本チャンサーバーの反応を見たいと思います。