多线程 IOCP 客户端问题

Posted

技术标签:

【中文标题】多线程 IOCP 客户端问题【英文标题】:Multithreaded IOCP Client Issue 【发布时间】:2010-06-11 03:47:20 【问题描述】:

我正在编写一个使用 IO 完成端口的多线程客户端。

我创建并连接了设置了 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.");

我将 IO 完成端口与套接字相关联。

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.");

WSASend 不会返回 SOCKET_ERROR 并将最后一个错误设置为 WSA_IO_PENDING,而是立即返回。

我需要 IO 挂起并在我的线程函数中处理它的完成,这也是我的工作线程。

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



我以前做过这个,但我不知道在这种情况下出了什么问题,我非常感谢帮助我解决这个问题的任何努力。

【问题讨论】:

【参考方案1】:

除非你这样做,否则这不是问题。

只要您不调用SetFileCompletionNotificationModes() 并设置标志以在成功时跳过完成端口处理,那么即使 WSARecv(或其他)返回 SUCCESS,IO 完成数据包也会像 ERROR_IO_PENDING 一样排队到 IOCP回来。因此,您不需要对非错误返回情况进行特殊处理。

详情请见http://support.microsoft.com/default.aspx?scid=kb;en-us;Q192800。

【讨论】:

问题是 WSARecv 立即返回而不是发布到 IOCP 队列。返回的最后一个错误是 0,因为它应该是 ERROR_IO_PENDING。我已经通过使用 ConnectEx 而不是 WSAConnect 解决了这个问题。谢谢你的回复:) 我的观点是 WSARecv 可以合法地返回 0 并且它仍然以与返回 ERROR_IO_PENDING 时相同的方式发布 IO 完成,除非您通过调用 SetFleCompletionNotificationModes() 调整了它的工作方式,它允许您告诉它在返回成功时不要发布 IO 完成...如果您使用适当的标志调用了 SetFileCompletionNotificationModes(),您只需要具有成功返回的特殊情况代码。 我刚刚又读了一遍你的帖子。我相信它返回 0 并且仍然没有发布到 IOCP 队列。【参考方案2】:

首先将调用分解成更清晰的逻辑:

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

另外,正如您在我的代码中看到的,您不应该根据WSASend 传递“&bytesSent”参数:

如果 lpOverlapped 参数不为 NULL 避免潜在的错误结果。

此外,您对 WSASend() 的调用看起来还不错。

【讨论】:

以上是关于多线程 IOCP 客户端问题的主要内容,如果未能解决你的问题,请参考以下文章

IOCP 浅析(java代码实现)

当服务器基于 iocp 时,我是不是需要让客户端支持 iocp?

ConnectEx 与 IOCP 问题

IOCP关键部分设计

DELPHI中完成端口(IOCP)的简单分析

c++ 求助socket多线程网络通信怎么实现并发