我想使用工作线程池和 IO 完成端口编写服务器。服务器应该在多个客户端之间处理和转发消息。“每个客户端”数据位于 ClientContext 类中。此类实例之间的数据使用工作线程进行交换。我认为这是一个典型的场景。
但是,这些 IO 完成端口有两个问题。
(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;
}
}
}