Проблема с многопоточным клиентом IOCP

Я пишу многопоточный клиент, который использует порт завершения ввода-вывода.

Я создаю и подключаю сокет с установленным атрибутом WSA_FLAG_OVERLAPPED.

if ((m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
    throw std::exception("Failed to create socket.");
}

if (WSAConnectByName(m_socket, L"server.com", L"80", &localAddressLength, reinterpret_cast<sockaddr*>(&localAddress), &remoteAddressLength, &remoteAddress, NULL, NULL) == FALSE)
{
    throw std::exception("Failed to connect.");
}

Я связываю порт завершения ввода-вывода с сокетом.

if ((m_hIOCP = CreateIoCompletionPort(reinterpret_cast<HANDLE>(m_socket), m_hIOCP, NULL, 8)) == NULL)
{
    throw std::exception("Failed to create IOCP object.");
}

Кажется, все идет хорошо, пока я не попытаюсь отправить данные через сокет.

SocketData* socketData = new SocketData;
socketData->hEvent = 0;

DWORD bytesSent = 0;
if (WSASend(m_socket, socketData->SetBuffer(socketData->GenerateLoginRequestHeader()), 1, &bytesSent, NULL, reinterpret_cast<OVERLAPPED*>(socketData), NULL) == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
    throw std::exception("Failed to send data.");
}

Вместо того, чтобы возвращать SOCKET_ERROR с последней ошибкой, установленной на WSA_IO_PENDING, WSASend немедленно возвращается.

Мне нужно, чтобы ввод-вывод был отложен, а его завершение обрабатывалось в моей функции потока, которая также является моим рабочим потоком.

unsigned int __stdcall MyClass::WorkerThread(void* lpThis)
{

}

Я делал это раньше, но я не знаю, что в этом случае не так, я был бы очень признателен за любые усилия, которые помогут мне решить эту проблему.


person Carl    schedule 11.06.2010    source источник


Ответы (2)


Это не проблема, если вы этого не сделаете.

Пока вы не вызываете SetFileCompletionNotificationModes () и установка флага для пропуска обработки порта завершения в случае успеха, тогда, даже если WSARecv (или что-то еще) возвращает УСПЕХ, пакет завершения ввода-вывода ставится в очередь IOCP так же, как если бы был возвращен ERROR_IO_PENDING. Таким образом, вам не требуется специальной обработки для случая возврата без ошибки.

См. http://support.microsoft.com/default.aspx?scid=kb;en-us;Q192800 для получения дополнительной информации.

person Len Holgate    schedule 13.06.2010
comment
Проблема заключалась в том, что WSARecv возвращался немедленно, а не отправлял в очередь IOCP. Последняя возвращенная ошибка была 0, как и должно быть ERROR_IO_PENDING. Я решил проблему, используя ConnectEx вместо WSAConnect. Спасибо за ваш ответ :) - person Carl; 14.06.2010
comment
Я хочу сказать, что WSARecv МОЖЕТ ЮРИДИЧЕСКИ вернуть 0, а ОН ВСЕ ЕЩЕ отправляет завершение ввода-вывода таким же образом, как и при возврате ERROR_IO_PENDING, если только вы не изменили его работу, вызвав SetFleCompletionNotificationModes (), который позволяет вам сказать ему НЕ публиковать Завершение ввода-вывода, когда оно возвращает успех ... У вас должен быть ТОЛЬКО специальный код для успешного возврата, ЕСЛИ вы вызвали SetFileCompletionNotificationModes () с соответствующими флагами. - person Len Holgate; 14.06.2010
comment
Я только что снова перечитал твой пост. Я считаю, что он возвращал 0 и все еще не отправлял в очередь IOCP. - person Carl; 15.06.2010

Прежде всего разбейте звонок на более понятную логику:

int nRet = WSASend(m_socket, socketData->SetBuffer(socketData->GenerateLoginRequestHeader()), 1, NULL, NULL, reinterpret_cast<OVERLAPPED*>(socketData), NULL);
if (nRet == SOCKET_ERROR)
{
    if ((WSAGetLastError()) == WSA_IO_PENDING)
        nRet = 0; // ok
    else
        throw std::exception("Failed to send data."); // failed
}

Кроме того, как вы можете видеть в моем коде, НЕ следует передавать параметр «& bytesSent» согласно WSASend:

Используйте NULL для этого параметра, если параметр lpOverlapped не равен NULL, чтобы избежать потенциально ошибочных результатов.

Кроме того, ваш вызов WSASend () выглядит нормально.

person Poni    schedule 13.06.2010