0

現在、マネージ C++ で winsock サーバー側ソケットを作成しています。LPWSAOVERLAPPED オブジェクトを作成して WSASend 関数に渡した後、操作が非ブロック化を完了したときに削除する場所がわかりません (WSASend は SOCKET_ERROR を返し、WSAGetLastError() は WSA_IO_PENDING を返します)。私の現在の解決策は、System::Threading::WaitHandle を作成し、待機ハンドルへの安全でないポインターを取得し、それを LPWSAOVERLAPPED オブジェクトの下の hEvent に渡すことでした。ただし、送信操作がいつ完了するかはあまり気にしないため、これは不要なオブジェクトの作成を引き起こしています。一方、操作を完全にノンブロッキングにするためには、LPWSAOVERLAPPED オブジェクトが必要です。これを解決するためのより良い解決策はありますか? これが私の現在のコードです:

void Connectivity::ConnectionInformation::SendData(unsigned char data[], const int length)
{
    if (isClosed || sendError)
        return;

    Monitor::Enter(this->sendSyncRoot);
    try
    {
        LPWSAOVERLAPPED overlapped = OverlappedObjectPool::GetOverlapped();
        WaitHandle ^ handle = gcnew ManualResetEvent(false);
        IntPtr handlePointer = handle->SafeWaitHandle->DangerousGetHandle();

        sendInfo->buf = (char*)data;
        sendInfo->len = length;

        overlapped->Internal = 0;
        overlapped->InternalHigh = 0;
        overlapped->Offset = 0;
        overlapped->OffsetHigh = 0;
        overlapped->Pointer = 0;
        overlapped->hEvent = (void*)handlePointer;                      //Set pointer

        if (WSASend(connection, sendInfo, 1, NULL, 0, overlapped, NULL) == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSA_IO_PENDING)
            {
                ThreadPool::UnsafeRegisterWaitForSingleObject(handle, sentCallback, (IntPtr)((void*)overlapped), -1, true);
            }
            else
            {
                this->sendError = true;
                //The send error bool makes sure that the close function doesn't get called
                //during packet processing which could lead to a lot of null reffernce exceptions.
                OverlappedObjectPool::GiveObject(overlapped);
            }
        }
        else
        {
            handle->Close();
            sentData((IntPtr)((void*)overlapped), false);
        }
    }
    finally
    {
        Monitor::Exit(this->sendSyncRoot);
    }
}   
4

1 に答える 1

1

非同期 I/O の場合、完了は、完了ルーチンの呼び出し、または IOCP 完了メッセージの IOCP 完了キューへのキューイングによって通知されます。どちらの場合も、OVL 構造体は少なくとも非同期操作全体の有効期間を持つ必要がありますが、都合がよければ長くなる可能性があることに注意してください:)

完了ルーチンの場合、OVL の未使用の hEvent パラメータを使用して、データ バッファ、WSABUF 配列、および OVL 構造体をメンバとして含む「IOrequest」クラス インスタンスへのポインタを転送できます (また、確実にI/O が発行されたソケット オブジェクトへのポインタ)。OVL ポインタは完了ルーチンへのパラメータとして提供されるため、hEvent を取得してクラス タイプにキャストできるため、完全なクラス インスタンス (OVL、データ バッファなど) を取得できます。データが処理されたとき (またはすぐにWSASend の場合は完了ルーチン)、この IOrequest は最終的に破棄 (または再プール) され、OVL はそれに伴います。これは少し近親相姦のように聞こえますが、問題なく動作し、厄介なマクロやその他のトリックは必要ありません。

同様のアプローチを完全な IOCP で使用するか、または lpCompletionKey 'spare' パラメータとして渡された OVL で使用できます。

ああ、操作が完了したかどうかは気にしません。少なくともエラーをチェックする必要があります。

于 2012-12-28T00:46:35.123 に答える