Я создаю TCP-сервер Visual C ++ WinSock с использованием BindIoCompletionCallback, он прекрасно работает при получении и отправке данных, но не могу найти хороший способ определения времени ожидания: SetSockOpt / SO_RCVTIMEO / SO_SNDTIMEO не влияет на неблокирующие сокеты, если узел не отправляет никаких данных, процедура CompletionRoutine вообще не вызывается.
Я думаю об использовании RegisterWaitForSingleObject с полем hEvent в OVERLAPPED, это может сработать, но тогда 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 здесь не поможет, я думаю, что я должен использовать поле hEvent в структуре OVERLAPPED, которая будет сигнализироваться после завершения ввода-вывода, но WaitForSingleObject / WSAWaitForMultipleEvents для этого будет блокировать вызов Receive и Получение всегда возвращается немедленно, поэтому я использовал RegisterWaitForSingleObject и WAITORTIMERCALLBACK. это сработало, обратный вызов был вызван по истечении времени ожидания, или IO завершился, но теперь у меня есть два обратных вызова для любой отдельной операции IO, CompletionRoutine и WaitOrTimerCallback:
если IO завершен, они будут вызваны одновременно, если IO не завершен, будет вызван WaitOrTimerCallback, затем я вызываю CancelIoEx, это вызвало вызов CompletionRoutine с некоторой ошибкой ABORTED, но здесь условие гонки, может быть, IO будет завершено как раз перед тем, как я отменю его, тогда ... бла, все в целом довольно сложно.
Тогда я понял, что мне вообще не нужны BindIoCompletionCallback и CompletionRoutine, и я делаю все с помощью WaitOrTimerCallback, это может сработать, но вот интересный вопрос, я хотел сначала создать Winsock-сервер на основе IOCP, и Я думал, что BindIoCompletionCallback - это самый простой способ сделать это, используя пул потоков, предоставляемый самой Windows, и теперь я вообще получаю сервер без кода IOCP? это все еще IOCP? или я должен забыть BindIoCompletionCallback и построить свою собственную реализацию пула потоков IOCP? почему?