2009-11-20 89 views
4

我想写一个使用工作线程池和IO完成端口的服务器。服务器应该在多个客户端之间处理和转发消息。 '每客户端'数据在类ClientContext中。使用工作线程交换此类的实例之间的数据。我认为这是一个典型的情况。IO完成端口:WSARecv()如何工作?

但是,这些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; 
     } 
    } 
} 

回答

8

(1)第一个问题是, 服务器基本上是从 客户端接收数据,但我从来不知道,如果接收到一个完整的 消息。

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

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

我希望它只是意味着你必须在你的代码中的错误...

请注意,这是“更好”从一个可扩展性点不以重叠的结构使用事件,而是关联带有IOCP的套接字,并允许将完成内容发布到处理完成的线程池。

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