2

ちょっと... I/O 完了ポートと winsock を使用して小さなテスト サーバーを作成しました。ソケットハンドルを完了ポートに正常に接続して関連付けることができます。しかし、ユーザー定義のデータ構造をワーカースレッドに渡す方法がわかりません...

私がこれまでに試した(ULONG_PTR)&structure asのは、の関連付け呼び出しで完了キーとしてユーザー構造を渡すCreateIoCompletionPort() ことでしたが、うまくいきませんでした。

ここで説明されているように、独自の OVERLAPPED 構造を定義し、CONTAINING_RECORD() を使用してみましたhttp://msdn.microsoft.com/en-us/magazine/cc302334.aspxおよびhttp://msdn.microsoft.com/en-us /magazine/bb985148.aspx . しかし、それもうまくいきません。(pHelper の内容に奇妙な値が表示されます)

私の質問は次のとおりです: WSARecv()、GetQueuedCompletionStatus() および Completion パケットまたは OVERLAPPED-strucutre を使用してワーカー スレッドにデータを渡すにはどうすればよいですか?

編集:「接続ごとのデータ」を正常に送信するにはどうすればよいですか?... (上記の 2 つのリンクで説明されているように) それを行う技術が間違っているようです。

ここに私のコードがあります:(はい、それは醜い唯一のテストコードです)

struct helper
    {
        SOCKET m_sock;
        unsigned int m_key;
        OVERLAPPED over;
    };


///////

SOCKET newSock = INVALID_SOCKET;
    WSABUF wsabuffer;
    char cbuf[250];
    wsabuffer.buf = cbuf;
    wsabuffer.len = 250;
    DWORD flags, bytesrecvd;


    while(true)
    {
        newSock = accept(AcceptorSock, NULL, NULL);
        if(newSock == INVALID_SOCKET)
            ErrorAbort("could not accept a connection");

        //associate socket with the CP
        if(CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, 3,0) != hCompletionPort)
            ErrorAbort("Wrong port associated with the connection");
        else
            cout << "New Connection made and associated\n";

        helper* pHelper = new helper;
        pHelper->m_key = 3;
        pHelper->m_sock = newSock;
        memset(&(pHelper->over), 0, sizeof(OVERLAPPED));
        flags = 0;
        bytesrecvd = 0;

        if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, (OVERLAPPED*)pHelper, NULL) != 0)
        {
            if(WSAGetLastError() != WSA_IO_PENDING)
                ErrorAbort("WSARecv didnt work");
        }
    }

    //Cleanup
    CloseHandle(hCompletionPort);
    cin.get();
    return 0;
}

DWORD WINAPI ThreadProc(HANDLE h)
{
    DWORD dwNumberOfBytes = 0;
    OVERLAPPED* pOver = nullptr;
    helper* pHelper = nullptr;
    WSABUF RecvBuf;
    char cBuffer[250];
    RecvBuf.buf = cBuffer;
    RecvBuf.len = 250;
    DWORD dwRecvBytes = 0;
    DWORD dwFlags = 0;
    ULONG_PTR Key = 0;

    GetQueuedCompletionStatus(h, &dwNumberOfBytes, &Key, &pOver, INFINITE);

    //Extract helper
    pHelper = (helper*)CONTAINING_RECORD(pOver, helper, over);


    cout << "Received Overlapped item" << endl;
    if(WSARecv(pHelper->m_sock, &RecvBuf, 1, &dwRecvBytes, &dwFlags, pOver, NULL) != 0)
        cout << "Could not receive data\n";
    else
        cout << "Data Received: " << RecvBuf.buf << endl;

    ExitThread(0);
}
4

3 に答える 3

4

このように構造体を渡すと、問題なく動作するはずです。

helper* pHelper = new helper;
CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, (ULONG_PTR)pHelper,0);
...


helper* pHelper=NULL;
GetQueuedCompletionStatus(h, &dwNumberOfBytes, (PULONG_PTR)&pHelper, &pOver, INFINITE);

編集して IO データごとに追加します。

頻繁に悪用される非同期 API の機能の 1 つは、OVERLAPPED 構造体をコピーせず、単に提供された構造体を使用することです。したがって、GetQueuedCompletionStatus から返されたオーバーラップした構造体は、最初に提供された構造体を指します。そう:

struct helper {
  OVERLAPPED m_over;
  SOCKET     m_socket;
  UINT       m_key;
};

if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, &pHelper->m_over, NULL) != 0)

繰り返しますが、元のサンプルでは、​​キャストが間違っていたことに注意してください。(OVERLAPPED*)pHelper はヘルパー構造体の START へのポインターを渡していましたが、OVERLAPPED 部分が最後に宣言されました。実際の重複部分のアドレスを渡すように変更しました。これは、コードがキャストなしでコンパイルされることを意味します。これにより、正しいことを行っていることがわかります。また、オーバーラップした構造体を移動して、構造体の最初のメンバーにしました。

反対側でデータをキャッチするには:

OVERLAPPED* pOver;
ULONG_PTR key;
if(GetQueuedCompletionStatus(h,&dw,&key,&pOver,INFINITE))
{
  // c cast
  helper* pConnData = (helper*)pOver;

この側では、オーバーラップされた構造体がヘルパー構造体の最初のメンバーであることが特に重要です。これにより、API が提供する OVERLAPPED* と実際に必要なヘルパー* から簡単にキャストバックできます。

于 2010-12-03T10:24:40.020 に答える
2

PostQueuedCompletionStatusを介して、独自の専用データを完了ポートに送信できます。

I/O 完了パケットは、GetQueuedCompletionStatus 関数への未解決の呼び出しを満たします。この関数は、PostQueuedCompletionStatus への呼び出しの 2 番目、3 番目、および 4 番目のパラメーターとして渡された 3 つの値を返します。システムはこれらの値を使用または検証しません。特に、lpOverlapped パラメータは OVERLAPPED 構造を指す必要はありません。

于 2010-12-03T12:01:19.767 に答える
0

OVERLAPPED 構造を使用できるため、作成/破棄には標準のソケット ルーチン (socket、closesocket、bind、accept、connect ...) を使用し、I/O には ReadFile/WriteFile を使用します。

ソケットが受け入れられるか接続されたら、それがサービスを提供するセッション コンテキストに関連付ける必要があります。次に、ソケットを IOCP に関連付け、(3 番目のパラメーターで) セッション コンテキストへの参照を指定します。IOCP は、この参照が何であるかを認識しておらず、そのことも気にしません。GetQueuedCompletionStatus を介して IOC を取得すると、パラメーター 3 が指す変数に参照が入力されるため、ソケット イベントに関連付けられたコンテキストをすぐに見つけて、イベントの処理を開始できます。私は通常、(特に) ソケット宣言、重複した構造、およびその他のセッション固有のデータを含むインデックス付き構造を使用します。

GetQueuedCompletionStatus が完了またはタイムアウトを返したかどうかを確認する必要があります。タイムアウトを使用すると、インデックス付き構造を実行して、(たとえば) そのうちの 1 つがタイムアウトしたかどうかを確認し、適切なハウスキーピング アクションを実行できます。

I/O が正しく完了したことを確認するために、重複した構造もチェックする必要があります。

IOCP を処理する関数は、独立したマルチスレッド エンティティである必要があります。システムのコアと同じ数のスレッドを使用するか、システム リソースを浪費するため、少なくともそれを超えないようにしてください (イベントを処理するためのリソースは、システムのコア数よりも多くありませんよね?) .

IOCP は本当にすべての世界で最高のものであり (真実であるには良すぎる)、「ソケットごとに 1 つのスレッド」または「1 つの関数で複数のソケット リストを待機する」と言う人は、彼らが何について話しているのかわかりません。前者はスケジューラにストレスを与え、後者はポーリングであり、ポーリングは常に非常に無駄です。

于 2010-12-14T08:06:47.473 に答える