1

オーバーラップ ソケット I/O を使用する、ATL を使用して C++ で実装された COM コンポーネントがあります。サーバーへの接続が確立された直後に、次のようなコードを使用して、ソケットでオーバーラップ読み取りを開始します。

// 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()非同期オーバーラップ読み取りで予想されるように、return SOCKET_ERROR、およびreturns 。WSAGetLastError()WSA_IO_PENDING

SysInternals TcpView アプリを使用して何が起こっているかを監視すると、クライアントがデータを受信して​​いることがわかります。しかし、コールバックが呼び出されることはありません。

接続されたソケットを介してサーバーにデータを送信すると、正常に機能します。

MFC アプリのメソッドでCoInitializeEx()andを呼び出しています。WSAStartup()InitInstance()

何か案は?

4

2 に答える 2

1

はい、これは確かにそうです。APCは、スレッドが「アラート可能な」待機状態になったときにのみ処理されます。つまり、またはSleepEx関数を呼び出します。WaitForMultipleObjectsExMsgWaitForMultipleObjectsEx

訂正したい点の1つは、OVERLAPPEDhEventメンバーを「ユーザー」データのプレースホルダーとして使用することは悪い考えであるということです。I / Oが完了すると、OSがこの「イベント」を設定しようとするためです。

コールバックルーティングに「ユーザー」情報を渡す一般的な方法は、実際には、に取って代わるカスタム構造を使用し、OVERLAPPED必要に応じてメンバーを追加することです(別名OVERLAPPED_PLUS)。次に、コールバックルーティングがにキャストさOVERLAPPEDれる可能性OVERLAPPED_PLUSがあります。そこにすべてのメンバーが表示されます。

もう1つのポイント:COMオブジェクトを作成しているため、独自のメッセージループを作成する機能がない可能性があります。したがって、アラート可能な待機の開始を保証するのは難しい場合があります。

于 2010-10-25T14:31:35.663 に答える
0

OK、WSARecv に関する他のスタック オーバーフローの質問を検索して、答えを見つけました。

Win32 Overlapped I/Oに対する Len Holgate の回答から- 完了ルーチンまたは WaitForMultipleObjects? :

. . . 完了が発生したときに呼び出される完了ルーチンを渡すことができます。これは「アラート可能な I/O」と呼ばれ、完了ルーチンを呼び出すには、WSARecv() 呼び出しを発行したスレッドが「アラート可能な」状態にある必要があります。スレッドは、いくつかの方法 (SleepEx() または待機関数のさまざまな EX バージョンの呼び出しなど) でアラート可能な状態にすることができます。. . .

を定期的に呼び出す MFC アプリでタイマーを開始するとSleepEx(0, TRUE)、受信コールバックが呼び出されることがわかりました。したがって、問題は、呼び出す COM オブジェクトのメソッドを呼び出すメイン MFC スレッドが、WSARecv()それ自体ではアラート可能な状態にならないことです。

そのため、おそらく COM オブジェクトの実装を変更して、コールバックではなく I/O 完了ポートを使用するか、呼び出しWSARecv()て自身をアラート状態に保つ独自のスレッドを開始する必要があります。

于 2010-10-25T14:18:50.237 に答える