非ブロッキングソケットのrecvについて
現在、Winsock2を使ってあるサーバーのプログラムを作成しています。
WSAEventSelect関数を使って非ブロッキングソケットを扱っているのですが、複数のクライアントからほぼ同時に接続要求などがあった際に
接続を取りこぼしてしまいます。
どなたかアドバイスいただけませんでしょうか。
以下、ソースを示します。プログラムの動作をわかりやすくするためエラー処理などは省略して記載します。(実際には行っています)
ちなみに非ブロッキングソケットを使用する理由は以下のとおりです。
・GUIアプリのワーカースレッドで動作させており、ブロッキングソケットでブロック中にメインスレッドが終了してもワーカースレッドがそれを知る術がなくワーカースレッドを安全に終了させることができないため。
/* 変数の宣言*/
int iRet=-1;
SOCKET ListenSock;
SOCKET AcceptSock;
int iRcvClientLen=0;
int iRcvLen=0;
char caRcvDat[1024]={0};
WSAEVENT hEvent;
DWORD dwResult;
WSANETWORKEVENTS events;
/* リッスンソケットを作成*/
ListenSock = socket(AF_INET, SOCK_STREAM, 0);
/* イベントのクリエイト*/
hEvent = WSACreateEvent();
/* リッスンソケットの設定*/
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT_NUM); /*PORT_NUMは定数*/
addr.sin_addr.S_un.S_addr = INADDR_ANY;
/* リッスンソケットをバインド*/
iRet = bind(ListenSock, (struct sockaddr *)&addr, sizeof(addr));
/* リッスンソケットでポートを開く*/
iRet = listen(ListenSock, 10);
/* クライアント接続待ちの無限ループ*/
while (1) {
printf("\n\n/--------- 待機中 ---------/\n\n");
iRcvClientLen = sizeof(client);
/* リッスンソケットにACCEPTイベントを設定*/
iRet = WSAEventSelect(ListenSock, hEvent, FD_ACCEPT);
/* ACCEPTイベント発生まで待機*/
dwResult = WSAWaitForMultipleEvents(1, &hEvent, FALSE, WSA_INFINITE, FALSE);
/* 発生したイベントを解析*/
iRet = WSAEnumNetworkEvents(ListenSock, hEvent, &events);
/* イベント変数をリセット*/
WSAResetEvent(hEvent);
/* 発生したイベントがACCEPTであれば接続を受け入れる*/
if(events.lNetworkEvents & FD_ACCEPT){
AcceptSock = accept(ListenSock, (struct sockaddr *)&client, &iRcvClientLen);
}
/* ACCEPTしたソケットにREADイベントを設定しなおす*/
iRet = WSAEventSelect(AcceptSock, hEvent, FD_READ);
/* READイベント発生まで待機*/
dwResult = WSAWaitForMultipleEvents(1, &hEvent, FALSE, WSA_INFINITE, FALSE);
/* 発生したイベントを解析*/
iRet = WSAEnumNetworkEvents(AcceptSock, hEvent, &events);
/* イベント変数をリセット*/
WSAResetEvent(hEvent);
/* 発生したイベントがREADであればデータを読み込む*/
if(events.lNetworkEvents & FD_READ){
iRcvLen=recv(AcceptSock, caRcvDat, 1024, 0);
/* ・・・以下Recv後の動作・・・*/
}
}
おそらく、2回目のWSAWaitForMultipleEvents関数で待機している間に接続要求が来たクライアントを取りこぼしているのだと思いますが、
対処方法がわかりません。
お礼
ご返答ありがとうございました。自己解決いたしました。 どういった状況であったかだけ説明させていただきますと、自身の作成し た送受信処理クラスにおいてrecv時のバッファサイズをメンバ変数に持た せていたのですが、その変数が「0」で初期化されていたままでした。 その変数はオーバーライドされたコンストラクタで任意の値に設定できる ようにしてあるのですが、それを行っていませんでした。つまりクラスの インスタンスが作成されていただけで安心していたわけです。 その結果、recvで受信するデータのサイズが0バイトということになって おり、受信可能データが全く減らないということになりFD_READイベント がなくなることもなく無限ループになっていたという落ちでした。 しかしrecvにこのような落とし穴というか、あくまで私自身が気づきに くいケースに遭遇できたという点で勉強になりました。