2009-11-20 16 views
4

Je souhaite écrire un serveur en utilisant un pool de threads de travail et un port d'achèvement d'E/S. Le serveur doit traiter et transférer les messages entre plusieurs clients. Les données 'par client' sont dans une classe ClientContext. Les données entre les instances de cette classe sont échangées à l'aide des threads de travail. Je pense que c'est un scénario typique.IO Achèvement des ports: comment fonctionne WSARecv()?

Cependant, j'ai deux problèmes avec ces ports d'achèvement d'E/S.

(1) Le premier problème est que le serveur reçoit essentiellement des données des clients mais je ne sais jamais si un message complet a été reçu. En fait WSAGetLastError() renvoie toujours que WSARecv() est toujours en attente. J'ai essayé d'attendre l'événement OVERLAPPED.hEvent avec WaitForMultipleObjects(). Cependant, il bloque pour toujours, c'est-à-dire que WSARecv() ne se termine jamais dans mon programme. Mon but est d'être absolument sûr que tout le message a été reçu avant que le traitement ne commence. Mon message a un champ 'longueur de message' dans son en-tête, mais je ne vois pas vraiment comment l'utiliser avec les paramètres de la fonction IOCP. (2) Si WSARecv() est mis en commentaire dans l'extrait de code ci-dessous, le programme reçoit toujours des données. Qu'est-ce que ça veut dire? Cela signifie-t-il que je n'ai pas besoin d'appeler WSARecv() du tout? Je ne suis pas en mesure d'obtenir un comportement déterministe avec ces ports d'achèvement d'E/S. Merci pour votre aide!

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; 
     } 
    } 
} 

Répondre

8

(1) Le premier problème est que le serveur reçoit essentiellement des données de clients, mais je ne sais jamais si un message complet a été reçu.

Vos appels recv peuvent retourner n'importe où de 1 octet à l'ensemble « message ». Vous devez inclure une logique qui fonctionne quand il a suffisamment de données pour calculer la longueur du «message» complet et ensuite travailler quand vous avez réellement un «message» complet. Alors que vous n'avez PAS assez de données, vous pouvez relancer un appel recv en utilisant le même tampon mémoire, mais avec une structure WSABUF mise à jour qui pointe vers la fin des données que vous avez déjà recvd. De cette façon, vous pouvez accumuler un message complet dans votre mémoire tampon sans avoir besoin de copier les données après chaque appel recv.

(2) Si WSARecv() est commenté dans l'extrait de code ci-dessous, le programme reçoit encore des données. Que signifie ? Cela signifie-t-il que je n'ai pas besoin de pour appeler WSARecv()?

Je pense que cela signifie que vous avez un bug dans votre code ...

Notez qu'il est « mieux » d'un point de vue de l'évolutivité de ne pas utiliser l'événement dans la structure chevauchée et au lieu d'associer la socket avec l'IOCP et permettre aux complétions d'être postées dans un pool de threads qui s'occupe de vos complétions.

J'ai un framework client/serveur IOCP gratuit disponible à partir de here qui peut vous donner quelques indications; et une série d'articles sur CodeProject (le premier est ici: http://www.codeproject.com/KB/IP/jbsocketserver1.aspx) où je traite l'ensemble du problème de "lecture des messages complets" (voir "Chunking the byte stream").