2017-09-07 8 views
0

J'implémente la communication de socket TCP en utilisant epoll pour surveiller tous les événements client, un seul thread traite tout le client dans une boucle for. chaque socket est non-bloquant.tcp socket recv retourne "ressource temporairement indisponible" quand recv buf a déjà des données

maintenant je viens de souffrir d'un problème, lorsque le client envoie des données plus que MTU, signifie plusieurs paquets de fragments, le serveur ne peut toujours pas lire toutes les données complètement. comme ci-dessous, j'ai lu la tête d'abord, obtenir le pdu len de la tête, puis lire la partie pdu. Le problème est que je lis la tête avec succès, en suivant de près le pdu recv(), mais il renvoie toujours EAGAIN plusieurs fois. donc ma nouvelle tentative va casser. Parce que le serveur a besoin de traiter des milliers d'événements clients, je pense que c'est une grande consommation de performance de laisser la réessayer toujours continuer ce qui n'est pas tolérable. J'utilise tcpdump capturant les paquets du client, chaque paquet est fragmenté à 1448 octets maximum de données, mais la tête est seulement 5 octets, pourquoi je peux lire la tête avec succès, mais l'opération recv() de données suivante retournera EAGAIN? est-il possible recv retourner EAGAIN lorsque les données arrivent déjà le tampon recv? À mon avis, je peux lire les 5 premiers octets, donc doit avoir plus de données à lire dans le tampon recv.

peut être liée au processus d'assemblage dans la pile tcp/ip. Code est comme ci-dessous, chaque recdu pdu, besoin de 10 ou plus réessayer, peut-être le succès.

... 
#define HDR_LEN 5 
n = epoll(epfd, events, 1000, -1) 
for(i =0; i < n; i++) 
{ 
    uint8 pHdr[HDR_LEN] = {0}; 
    uint16 pdulen = 0, offset =0; 
    infd = events[i].fd; 
    nRead = recv(infd, pHdr, HDR_LEN); // read the data head first 
    pdulen = ntohs(*(uint16 *)(pHdr+2)); // get the pdu len from the head 
    uint8 *pbuf = malloc(pdulen+HDR_LEN); 

    memcpy(pbuf, pHdr, HDR_LEN);   // move the head to buf 
    while(offset != pdulen)     // then read the pdu data 
    { 
     nRead = recv(infd, pbuf+HDR_LEN+offset, pdulen-offset); 
     if (nRead <=0) 
     { 
      if (nRead == -1 && errno == EAGAIN) // resource temporarily unavailable 
      { 
       if (retry < 5) 
       { 
        usleep(500); 
        retry++; 
        continue; 
       } 
       else 
        break; // already try 5 times, should always continue? 
      } 
      else 
       break; 
     } 
     else 
     { 
      offset += nRead; 
      retry = 0; 
     } 
    } 
    if (offset == pdulen) 
     process(pbuf, pdulen+HDR_LEN); // process the complete data 
    ... 
} 
... 
+0

s'il vous plaît un peu d'aide, aider à sortir de ce problème! – jackxie

+1

Votre socket est-elle en mode non-bloquant? Si c'est le cas, alors EAGAIN doit être attendu chaque fois qu'il n'y a plus de données dans le tampon de réception à récupérer. Pour gérer simultanément les E/S non bloquantes de plusieurs sockets, vous devez implémenter une machine à états afin de pouvoir revenir à votre appel poll() normal après avoir reçu une réponse partielle. Ensuite, lorsque poll() indique qu'il y a plus de données à lire sur votre socket, alors (et seulement ensuite) devriez-vous revenir en arrière et recv() les données supplémentaires de la socket, en les ajoutant aux données que vous avez vous avez toute la PDU. –

+0

'tcp socket recv return "ressource temporairement indisponible" quand recv buf a déjà des données?' Non. – EJP

Répondre

0

epoll vous dira si vous pouvez recv sans bloquer, une fois. Si recv consomme toutes les données de la socket, alors recv va bloquer jusqu'à ce qu'il y ait plus de données, ou retourner EAGAIN (si socket non-bloquant).

Un motif commun est le suivant:

  1. Utilisation select/poll/epoll pour détecter le moment où on peut lire un support.
  2. Appelez recv une fois sur une socket prête et ajoutez les données reçues à un tampon.
  3. Vérifiez si le tampon contient suffisamment de données pour le traitement. Si oui, alors traitez. Sinon, laissez select/poll/epoll dire quand vous pouvez en lire plus.