IO 完成端口:WSARecv() 是如何工作的?

Posted

技术标签:

【中文标题】IO 完成端口:WSARecv() 是如何工作的?【英文标题】:IO Completion ports: How does WSARecv() work? 【发布时间】:2009-11-20 09:32:40 【问题描述】:

我想编写一个使用工作线程池和 IO 完成端口的服务器。服务器应该在多个客户端之间处理和转发消息。 “每个客户端”数据位于 ClientContext 类中。此类实例之间的数据使用工作线程进行交换。我认为这是一个典型的场景。

但是,这些 IO 完成端口有两个问题。

(1)第一个问题是服务器基本上是从客户端接收数据,但我不知道是否接收到完整的消息。事实上 WSAGetLastError() 总是返回 WSARecv() 仍然处于挂起状态。我尝试使用 WaitForMultipleObjects() 等待事件 OVERLAPPED.hEvent。但是,它永远阻塞,即 WSARecv() 在我的程序中永远不会完成。 我的目标是绝对确保在进一步处理开始之前已收到整个消息。我的消息在其标题中有一个“消息长度”字段,但我真的不知道如何将它与 IOCP 函数参数一起使用。

(2) 如果在下面的代码sn-p中注释掉了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;
         
     
 

【问题讨论】:

【参考方案1】:

(1) 第一个问题是 服务器基本上从 客户,但我不知道是否完整 消息已收到。

您的 recv 调用可以返回从 1 个字节到整个“消息”的任何位置。您需要包含逻辑,当它有足够的数据来计算完整的“消息”的长度时,然后在你真正有完整的“消息”时计算出来。虽然您没有足够的数据,但您可以使用相同的内存缓冲区但使用更新的 WSABUF 结构重新发出一个 recv 调用,该结构指向您已经 recvd 的数据的末尾。这样,您可以在缓冲区中累积完整的消息,而无需在每次 recv 调用完成后复制数据。

(2) 如果 WSARecv() 被注释掉 下面的代码sn-p,程序 仍然接收数据。那是什么 意思是?这是否意味着我不需要 要调用 WSARecv() 吗?

我认为这只是意味着您的代码中存在错误...

请注意,从可扩展性的角度来看,最好不要在重叠结构中使用事件,而是将套接字与 IOCP 相关联,并允许将完成发布到处理完成的线程池。

我有一个免费的 IOCP 客户端/服务器框架,可从here 获得,它可能会给你一些提示;以及关于 CodeProject 的一系列文章(第一篇在这里:http://www.codeproject.com/KB/IP/jbsocketserver1.aspx),其中我处理了整个“阅读完整消息”问题(请参阅“分块字节流”)。

【讨论】:

以上是关于IO 完成端口:WSARecv() 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

如何知道完成数据包是针对 WSASend() 还是 WSARecv() 还是 AcceptEx()?

使用套接字时,哪些 IO 操作会导致完成数据包发送到完成端口?

使用 io 完成端口时,WriteFile 是不是会在完成时立即发布完成数据包

Socket编程模型之完成端口模型

我可以在 IOCP WSARecv 函数中使用 std::string 吗?

完成端口