1

我有一个 COM 组件,用 C++ 和 ATL 实现,它使用重叠的套接字 I/O。与服务器建立连接后,它立即开始对套接字进行重叠读取,代码如下:

// Pass pointer to this instance as hEvent parameter, for use by callback
m_recvOverlapped.hEvent = reinterpret_cast<HANDLE>(this);

int rc = ::WSARecv(m_s, &wsabuf, 1, &m_recvNumberOfBytes, &m_recvFlags, &m_recvOverlapped, RecvCallback);
if (rc == SOCKET_ERROR)
{
    // If error is WSA_IO_PENDING, then the I/O is still in progress.  Otherwise, something bad happened.
    int error = ::WSAGetLastError();
    if (error != WSA_IO_PENDING)
    {
        ReceiveError(error);
    }
}

我有一个看起来像这样的回调函数:

void CALLBACK CMySocket::RecvCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
CMySocket* socket = reinterpret_cast<CMySocket*>(lpOverlapped->hEvent);
ATLASSERT(socket != 0);
if (!socket)
    return;

socket->ReceiveCompleted(dwError, cbTransferred, lpOverlapped, dwFlags);
}

此 COM 组件在单元测试中、在命令行应用程序中使用时以及在 .NET GUI 应用程序中使用时(通过 COM 互操作)都可以正常工作。但是,当我在 MFC 应用程序中使用此组件RecvCallback时,服务器向其发送数据时永远不会调用该组件。

WSARecv()ReturnsSOCKET_ERRORWSAGetLastError()Returns WSA_IO_PENDING,正如异步重叠读取所期望的那样。

当我使用 SysInternals TcpView 应用程序观察正在发生的事情时,它表明客户端正在接收数据。但是永远不会调用回调。

通过连接的套接字向服务器发送数据工作正常。

我正在调用CoInitializeEx()WSAStartup()在我的 MFC 应用程序的InitInstance()方法中。

有任何想法吗?

4

2 回答 2

1

是的,确实如此。仅当线程进入“警报”等待状态时才处理 APC - 调用SleepExor WaitForMultipleObjectsExorMsgWaitForMultipleObjectsEx函数。

我想纠正您的一点是,使用OVERLAPPED'shEvent成员作为“用户”数据的占位符是一个坏主意。因为操作系统会在你的 I/O 完成时尝试设置这个“事件”。

将一些“用户”信息传递给回调路由的常用方法实际上是使用自定义结构来取代OVERLAPPED,并根据需要添加更多成员(也称为OVERLAPPED_PLUS)。然后你的回调路由可能会投射OVERLAPPED到你的OVERLAPPED_PLUS,在那里你会看到所有的成员。

另一点:由于您正在编写一个 COM 对象 - 您可能没有能力编写自己的消息循环,因此可能难以保证进入警报等待。

于 2010-10-25T14:31:35.663 回答
0

好的,我通过搜索有关 WSARecv 的其他 Stack Overflow 问题找到了答案。

来自 Len Holgate 对Win32 Overlapped I/O - Completion routines or WaitForMultipleObjects的回答?:

. . . 您可以传递一个完成例程,该例程在完成时调用。这称为“可警报 I/O”,并要求发出 WSARecv() 调用的线程处于“可警报”状态,才能调用完成例程。线程可以通过多种方式将自己置于警报状态(调用 SleepEx() 或等待函数的各种 EX 版本等)。. . .

事实证明,如果我在 MFC 应用程序中启动一个定期调用的计时器SleepEx(0, TRUE),则会调用接收回调。所以问题是调用我的 COM 对象的调用方法的主 MFC 线程WSARecv()永远不会自行进入警报状态。

因此,我可能需要更改我的 COM 对象的实现,以便它使用 I/O 完成端口而不是回调,或者启动它自己的线程来调用WSARecv()并保持自身可警报。

于 2010-10-25T14:18:50.237 回答