私のソケット サーバーでは、WSAEventSelect() と WSAEnumNetworkEvents() を使用して FD_ACCEPT と FD_CLOSE イベントを検出します。
これが私のコードの簡略版です:
HANDLE sockEv=CreateEvent(NULL,TRUE,FALSE,NULL);
WSAEventSelect(servSocket,sockEv,FD_ACCEPT|FD_CLOSE);
for(;;) {
if(WSAWaitForMultipleEvents(1,&sockEv,FALSE,INFINITE,FALSE)==WSA_WAIT_EVENT_0) {
WSANETWORKEVENTS wsaEvents={0};
WSAEnumNetworkEvents(servSocket,sockEv,&wsaEvents);
if((wsaEvents.lNetworkEvents & FD_ACCEPT)==FD_ACCEPT) {
SOCKET clntSock=accept(servSocket,(sockaddr*)clientAddr,&addrlen);
....
}
else if (wsaEvents.lNetworkEvents==0) {
// this event occurs after each FD_ACCEPT event
}
....
}
....
}
問題は、クライアントから新しい接続が受け入れられるたびに、2 つのイベントを受信することです。最初に WSANETWORKEVENTS.lNetworkEvents が FD_ACCEP に設定され、次に WSANETWORKEVENTS.lNetworkEvents がゼロに設定されます。つまり、for(;;) ループは、新しいクライアントが受け入れられるたびに 2 回実行されます。または、FD_ACCEPT を検出した後、sockEv が WSAEnumNetworkEvents によってリセットされない可能性があります。
このゼロイベントは何を意味し、なぜそれが起こるのですか? ドキュメントでそれについて何も見つかりませんでした。
更新: コンパイル可能なサンプル
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
try {
SOCKET servSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
sockaddr_in serv={0};
serv.sin_family=AF_INET;
serv.sin_addr.s_addr=htonl(INADDR_ANY);
serv.sin_port=htons(18081);
memset(serv.sin_zero,0,sizeof(serv.sin_zero));
if(bind(servSocket,(sockaddr*)&serv,sizeof(serv))==SOCKET_ERROR)
throw WSAGetLastError();
if(listen(servSocket,SOMAXCONN)==SOCKET_ERROR)
throw WSAGetLastError();
HANDLE sockEv=CreateEvent(NULL,TRUE,FALSE,NULL);
if(WSAEventSelect(servSocket,sockEv,FD_ACCEPT|FD_CLOSE)==SOCKET_ERROR)
throw WSAGetLastError();
for(;;) {
if(WSAWaitForMultipleEvents(1,&sockEv,FALSE,INFINITE,FALSE)!=WSA_WAIT_EVENT_0)
throw -1;
WSANETWORKEVENTS wsaEvents={0};
if(WSAEnumNetworkEvents(servSocket,sockEv,&wsaEvents)==SOCKET_ERROR)
throw WSAGetLastError();
if((wsaEvents.lNetworkEvents & FD_ACCEPT)==FD_ACCEPT) {
SOCKET clntSocket=accept(servSocket,0,0);
closesocket(clntSocket);
puts("accept");
}
else if(wsaEvents.lNetworkEvents==0) {
puts("zero event");
}
}
}
catch(int errorCode) {
printf("Last Error: %d\n",errorCode);
}
WSACleanup();
return 0;
}
このコードを WinXP 32bit で試しました。