代码之家  ›  专栏  ›  技术社区  ›  Kristopher Johnson

MFC应用程序中未调用重叠的WSARecv()回调

  •  1
  • Kristopher Johnson  · 技术社区  · 14 年前

    // 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图形用户界面应用程序(通过COM互操作)中使用时工作正常。但是,当我在MFC应用程序中使用此组件时 RecvCallback 当服务器向其发送数据时从不被调用。

    WSARecv() SOCKET_ERROR ,和 WSAGetLastError() 回报 WSA_IO_PENDING ,与异步重叠读取的预期一致。

    当我使用SysInternals TcpView应用程序监视正在发生的事情时,它表示客户端正在接收数据。但回拨永远不会被调用。

    我在打电话 CoInitializeEx() WSAStartup() 在我的MFC应用程序中 InitInstance() 方法。

    有什么想法吗?

    2 回复  |  直到 14 年前
        1
  •  1
  •   valdo    14 年前

    是的,确实如此。只有当线程进入“可报警”等待状态时才处理APC—调用 SleepEx WaitForMultipleObjectsEx MsgWaitForMultipleObjectsEx 功能。

    我想纠正你的一点是 OVERLAPPED hEvent 成员作为“用户”数据的占位符是一个坏主意。因为OS会在I/O完成时尝试设置此“事件”。

    重叠的 ,根据需要添加更多成员(也称为。 OVERLAPPED_PLUS ). 那么你的回叫路由可能会 重叠的 给你的 重叠加号

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

        2
  •  0
  •   Peter Coulton    8 年前

    从伦霍尔盖特的回答到 Win32 Overlapped I/O - Completion routines or WaitForMultipleObjects? :

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

    SleepEx(0, TRUE) ,然后调用接收回调。所以问题是调用COM对象的方法的主MFC线程 WSARecv() 永远不要自己进入警戒状态。

    因此,我可能需要更改COM对象的实现,使其使用I/O完成端口而不是回调,或者启动自己的线程来调用 保持警觉。