I am creating a Visual C ++ WinSock TCP server using BindIoCompletionCallback, it works fine with receiving and sending data, but I cannot find a good way to determine the timeout: SetSockOpt / SO_RCVTIMEO / SO_SNDTIMEO does not affect non-blocking sockets if peer does not send no data, CompletionRoutine is not called at all.
I am thinking about using RegisterWaitForSingleObject with the hEvent OVERLAPPED field, which may work, but then CompletionRoutine is not required at all, am I still using IOCP? is there a performance issue if I use only RegisterWaitForSingleObject and not use BindIoCompletionCallback?
Update: Code Example:
My first attempt:
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; }
As I said, it works fine if the client really sends me the data, "Receive" does not block, CompletionRoutine received the call, the received data, but here is one of them, if the client does not send me any data, how can I refuse timeout?
Since SetSockOpt / SO_RCVTIMEO / SO_SNDTIMEO will not help here, I think I should use the hEvent field in the OVERLAPPED structure, which will be signaled when IO completes, but WaitForSingleObject / WSAWaitForMultipleEvents on this will block the receiving call, and I always want to Receive so I used RegisterWaitForSingleObject and WAITORTIMERCALLBACK. it worked, callback received a call after a timeout or I / O completion, but now I have two callbacks for any I / O operation, CompletionRoutine and WaitOrTimerCallback:
if the IO is completed, they will be called at the same time, if the IO is not completed, WaitOrTimerCallback will be called, then I call CancelIoEx, this caused the CompletionRoutine call with some ABORTED error, but here is the race condition, maybe the IO will be completed before I cancel it , then ... blabbal, it's all pretty complicated.
Then I realized that I really do not need BindIoCompletionCallback and CompletionRoutine in general, and to do everything from WaitOrTimerCallback, this might work, but here's an interesting question, I would like to create an IOCP-based Winsock server first, and thought BindIoCompletionCallback is the easiest way to do this using threadpool, which is supported by Windows itself, now I get a server without IOCP code at all? Is it still an IOCP? or should I forget the BindIoCompletionCallback and create my own implementation of IOPP threadpool? why?