BindIoCompletionCallbackを使用してVisualC++ WinSock TCPサーバーを構築しています。データの送受信は正常に機能しますが、タイムアウトを検出する適切な方法が見つかりません。ピアがブロックされていない場合、SetSockOpt / SO_RCVTIMEO/SO_SNDTIMEOは非ブロッキングソケットに影響を与えません。データを送信すると、CompletionRoutineはまったく呼び出されません。
OVERLAPPEDのhEventフィールドでRegisterWaitForSingleObjectを使用することを考えていますが、これは機能する可能性がありますが、CompletionRoutineはまったく必要ありません。まだIOCPを使用していますか?RegisterWaitForSingleObjectのみを使用し、BindIoCompletionCallbackを使用しない場合、パフォーマンスの問題はありますか?
更新:コードサンプル:
私の最初の試み:
bool CServer::Startup() {
SOCKET ServerSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
WSAEVENT ServerEvent = WSACreateEvent();
WSAEventSelect(ServerSocket, ServerEvent, FD_ACCEPT);
......
bind(ServerSocket......);
listen(ServerSocket......);
_beginthread(ListeningThread, 128 * 1024, (void*) this);
......
......
}
void __cdecl CServer::ListeningThread( void* param ) // static
{
CServer* server = (CServer*) param;
while (true) {
if (WSAWaitForMultipleEvents(1, &server->ServerEvent, FALSE, 100, FALSE) == WSA_WAIT_EVENT_0) {
WSANETWORKEVENTS events = {};
if (WSAEnumNetworkEvents(server->ServerSocket, server->ServerEvent, &events) != SOCKET_ERROR) {
if ((events.lNetworkEvents & FD_ACCEPT) && (events.iErrorCode[FD_ACCEPT_BIT] == 0)) {
SOCKET socket = accept(server->ServerSocket, NULL, NULL);
if (socket != SOCKET_ERROR) {
BindIoCompletionCallback((HANDLE) socket, CompletionRoutine, 0);
......
}
}
}
}
}
}
VOID CALLBACK CServer::CompletionRoutine( __in DWORD dwErrorCode, __in DWORD dwNumberOfBytesTransfered, __in LPOVERLAPPED lpOverlapped ) // static
{
......
BOOL res = GetOverlappedResult(......, TRUE);
......
}
class CIoOperation {
public:
OVERLAPPED Overlapped;
......
......
};
bool CServer::Receive(SOCKET socket, PBYTE buffer, DWORD length, void* context)
{
if (connection != NULL) {
CIoOperation* io = new CIoOperation();
WSABUF buf = {length, (PCHAR) buffer};
DWORD flags = 0;
if ((WSARecv(Socket, &buf, 1, NULL, &flags, &io->Overlapped, NULL) != 0) && (GetLastError() != WSA_IO_PENDING)) {
delete io;
return false;
} else return true;
}
return false;
}
私が言ったように、クライアントが実際に私にデータを送信している場合は正常に機能します、「受信」はブロックされていません、CompletionRoutineが呼び出され、データが受信されましたが、クライアントが私にデータを送信していない場合、どうすればよいですか?タイムアウト後にあきらめますか?
SetSockOpt / SO_RCVTIMEO / SO_SNDTIMEOはここでは役に立たないので、IOが完了したときに通知されるOVERLAPPED構造のhEventフィールドを使用する必要があると思いますが、その上のWaitForSingleObject / WSAWaitForMultipleEventsはReceive呼び出しをブロックし、Receiveを常にすぐに戻るので、RegisterWaitForSingleObjectとWAITORTIMERCALLBACKを使用しました。動作し、タイムアウト後にコールバックが呼び出されたか、IOが完了しましたが、単一のIO操作に対して2つのコールバック、CompletionRoutineとWaitOrTimerCallbackがあります。
IOが完了した場合、それらは同時に呼び出されます。IOが完了していない場合、WaitOrTimerCallbackが呼び出され、次にCancelIoExを呼び出します。これにより、CompletionRoutineが何らかのABORTEDエラーで呼び出されましたが、競合状態、おそらくIOです。キャンセルする直前に完了します。それから...何とか、全体として非常に複雑です。
次に、実際にはBindIoCompletionCallbackとCompletionRoutineはまったく必要ないことに気付き、WaitOrTimerCallbackからすべてを実行すると、機能する可能性がありますが、ここで興味深い質問があります。そもそもIOCPベースのWinsockサーバーを構築したかったので、BindIoCompletionCallbackはこれを行う最も簡単な方法は、Windows自体が提供するスレッドプールを使用して、IOCPコードがまったくないサーバーになってしまうのでしょうか。それはまだIOCPですか?または、BindIoCompletionCallbackを忘れて、独自のIOCPスレッドプール実装を構築する必要がありますか?なぜ ?