2017-05-26 5 views
1

TCP peut fusionner et diviser arbitrairement des paquets. Donc, en supposant que je fais, par exemple, un tel appel:Le bloc `recv` sera-t-il bloqué sur un socket TCP lorsque je demande plus de données que ce qui est immédiatement disponible?

`recv(sock, buf, 15, 0)` 

Mais à ce moment seulement 5 octets de données est immédiatement disponible -

bloquerons recv() jusqu'à ce que 15 octets de données sont disponibles ou seront Je reçois seulement 5 octets?

Je demande parce que je voudrais savoir si je peux lire, par exemple, un uint32_t simplement de cette façon (en supposant i est une variable de type uint32_t):

if(recv(sock, &i, sizeof(uint32_t), 0) < sizeof(uint32_t)) { 
    /* error */ 
} 
i = ntohl(i); 

Ou dois-je plutôt doivent faire quelque chose comme ça:

unsigned char buff[sizeof(uint32_t)]; 
ssize_t read_already = 0; 
while(read_already != sizeof(uint32_t)) { 
    ssize_t read_now = recv(sock, buff, sizeof(uint32_t)-read_already, 0); 
    if(read_now == -1) { 
     /* error */ 
    } 
    else { 
     read_already += read_now; 
    } 
} 
memcpy(&i, buff, sizeof(uint32_t)); 
i = ntohl(i); 

ce dernier est nettement plus laid et plus ardu, mais malheureusement si nécessaire recv() ne bloque pas jusqu'à ce qu'il reçoive toutes les données demandées dans le cas où le paquet est divisé.

Répondre

1

En général, sans indicateurs spéciaux, options de socket ou ioctls en cours de définition, un appel recv sur un socket TCP bloquant renvoie un nombre d'octets inférieur ou égal à la taille demandée. Mais à moins que le socket soit fermé à distance, interrompu par un signal, ou dans un état d'erreur, il se bloquera jusqu'à ce qu'au moins 1 octet soit disponible.

En d'autres termes, si vous demandez 15 octets, mais seulement 5 sont disponibles, recv retournerez 5.

Les développeurs d'applications ne doivent pas compter sur la façon dont les données ont été envoyées ou construit et traiter leur prise comme capable de renvoyer un flux partiel de données à tout moment. (Ou comme je le dis aux autres, écrivez votre code comme s'il était possible que recv ne retourne qu'un octet à la fois).

Une boucle commune pour la réception de données ressemble souvent à ce qui suit. Notez le pointeur math sur buffer dans l'appel recv.

unsigned char buffer[bytes_expected]; 
ssize_t bytes_received = 0; 
while (bytes_received < bytes_expected) 
{ 
    int result = recv(sock, buffer + bytes_received, bytes_expected-bytes_received, 0); 
    if (result == 0) 
    { 
     // socket was closed remotely - break out of the loop 
    } 
    else if (result < 0) 
    { 
     // socket was closed on remote end or hit an error 
     // either way, the socket is likely dead 
     break; 
    } 
    else 
    { 
     bytes_received += result; 
    } 
} 

L'exception à cette règle que je sais, c'est le drapeau MSG_WAITALL basé que le dernier paramètre à recv. Cela contiendra le socket (bloc) jusqu'à ce que vous obteniez tous les octets demandés par le paramètre length passé à recv (ou jusqu'à ce que l'erreur ou la fermeture du socket).

De l'man page for recv:

MSG_WAITALL (depuis Linux 2.2) Ce Requests drapeau que le bloc opération jusqu'à ce que la demande complète soit satisfaite. Cependant, l'appel peut encore retour moins de données que demandé si un signal est pris, une erreur ou déconnexion se produit, ou les prochaines données à recevoir est d'un type différent que retourné

Le fait que On vous demande même si cette question vous met dans une ligue au-dessus de la plupart des autres qui sont nouveaux pour les sockets. Il y a tellement de code buggé dans le monde réel parce que la plupart des développeurs n'ont pas géré le cas de recv retournant quelque chose de moins que prévu.

+0

Merci. Donc, il n'y a pas d'échappatoire à la laideur et la pénibilité de mon deuxième extrait? – gaazkam

+1

'MSG_WAITALL' pourrait répondre à vos besoins. Il y a un bug dans votre snippet (vous n'avez pas fait le calcul du pointeur sur buff dans votre question). Voir l'exemple de code que j'ai fourni. – selbie