ワーカースレッドのプールと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;
}
}
}