4

ワーカースレッドのプールとIO完了ポートを使用してサーバーを作成したいと思います。サーバーは、複数のクライアント間でメッセージを処理および転送する必要があります。'クライアントごとの'データはクラスClientContextにあります。このクラスのインスタンス間のデータは、ワーカースレッドを使用して交換されます。これは典型的なシナリオだと思います。

ただし、これらのIO完了ポートには2つの問題があります。

(1)最初の問題は、サーバーが基本的にクライアントからデータを受信することですが、完全なメッセージが受信されたかどうかはわかりません。実際、WSAGetLastError()は、常にWSARecv()がまだ保留中であることを返します。WaitForMultipleObjects()を使用してイベントOVERLAPPED.hEventを待機しようとしました。ただし、それは永久にブロックされます。つまり、私のプログラムではWSARecv()が完了しません。私の目標は、さらに処理を開始する前に、メッセージ全体が受信されたことを絶対に確認することです。私のメッセージのヘッダーには「メッセージの長さ」フィールドがありますが、IOCP関数パラメーターでメッセージを使用する方法がわかりません。

(2)以下のコードスニペットでWSARecv()がコメントアウトされている場合でも、プログラムはデータを受信します。どういう意味ですか?WSARecv()を呼び出す必要がまったくないということですか?これらのIO完了ポートで決定論的な動作を得ることができません。ご協力いただきありがとうございます!

while(WaitForSingleObject(module_com->m_shutdown_event, 0)!= WAIT_OBJECT_0)
{

    dequeue_result = GetQueuedCompletionStatus(module_com->m_h_io_completion_port,
                                               &transfered_bytes,
                                               (LPDWORD)&lp_completion_key,
                                               &p_ol,
                                               INFINITE);
     if (lp_completion_key == NULL)
     {
         //Shutting down
         break;
     }

     //Get client context
     current_context = (ClientContext *)lp_completion_key;

     //IOCP error
     if(dequeue_result == FALSE)
     {
         //... do some error handling...
     }
     else
     {   
         // 'per client' data
         thread_state = current_context->GetState();
         wsa_recv_buf = current_context->GetWSABUFPtr();

         // 'per call' data
         this_overlapped = current_context->GetOVERLAPPEDPtr();
     }

     while(thread_state != STATE_DONE)
     {
         switch(thread_state)
         {
         case STATE_INIT:

             //Check if completion packet has been posted by internal function or by WSARecv(), WSASend()
             if(transfered_bytes > 0)
             {
                 dwFlags = 0;
                 transf_now = 0;
                 transf_result = WSARecv(current_context->GetSocket(),
                                         wsa_recv_buf,
                                         1,
                                         &transf_now,
                                         &dwFlags,
                                         this_overlapped,
                                         NULL);

                 if (SOCKET_ERROR == transf_result && WSAGetLastError() != WSA_IO_PENDING)
                 {   
                     //...error handling...
                     break;
                 }

                 // put received message into a message queue

             }
             else // (transfered_bytes == 0)
             {
                 // Another context passed data to this context
                 // and notified it via PostQueuedCompletionStatus().
             }
             break;
         }
     }
 }
4

1 に答える 1

8

(1)最初の問題は、サーバーが基本的にクライアントからデータを受信することですが、完全なメッセージが受信されたかどうかはわかりません。

recv呼び出しは、1バイトから「メッセージ」全体までのどこにでも戻ることができます。完全な「メッセージ」の長さを計算するのに十分なデータがあるときに機能し、実際に完全な「メッセージ」があるときに機能するロジックを含める必要があります。十分なデータがない場合でも、同じメモリバッファーを使用して、既にrecvdしたデータの終わりを指す更新されたWSABUF構造を使用して、recv呼び出しを再発行できます。このようにして、すべてのrecv呼び出しが完了した後にデータをコピーする必要なしに、バッファーに完全なメッセージを蓄積できます。

(2)以下のコードスニペットでWSARecv()がコメントアウトされている場合でも、プログラムはデータを受信します。どういう意味ですか?WSARecv()を呼び出す必要がまったくないということですか?

コードにバグがあることを意味していると思います...

スケーラビリティの観点からは、オーバーラップした構造でイベントを使用せず、代わりにソケットをIOCPに関連付けて、完了を処理するスレッドプールに完了を投稿できるようにする方が「優れている」ことに注意してください。

私はここから無料のIOCPクライアント/サーバーフレームワークを利用できます。これはいくつかのヒントを与えるかもしれません。そして、CodeProjectに関する一連の記事(最初の記事はここにあります:http://www.codeproject.com/KB/IP/jbsocketserver1.aspx)では、「完全なメッセージの読み取り」の問題全体を扱っています(「バイトストリームのチャンク化」を参照)。 ")。

于 2009-11-20T13:06:08.637 に答える