2017-05-26 2 views
1

TCP kann Pakete beliebig zusammenführen und teilen. Also, vorausgesetzt, ich tue, zum Beispiel, wie ein Anruf:Blockiert `recv` auf einem TCP-Socket, wenn ich mehr Daten anfordere, als sofort verfügbar sind?

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

Aber in diesem Augenblick nur 5 Bytes von Daten ist ab sofort verfügbar -

Will recv() Block bis 15 Bytes von Daten zur Verfügung steht, oder wird Ich bekomme nur 5 Bytes?

ich gefragt bin, weil ich, wenn ich lesen kann, wissen möchten, zum Beispiel eine uint32_t einfach auf diese Weise (i unter der Annahme, eine Variable vom Typ ist uint32_t):

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

Oder muss ich eher haben, so etwas zu tun:

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

letztere deutlich hässlicher und mühsamer ist, aber leider notwendig, wenn recv() nicht blockiert, bis sie alle Daten im Fall angefordert erhält wird das Paket aufgeteilt.

Antwort

1

Ohne spezielle Flags, Socket-Optionen oder ioctls wird im Allgemeinen ein recv Aufruf auf einem blockierenden TCP-Socket eine beliebige Anzahl von Bytes zurückgeben, die kleiner oder gleich der angeforderten Größe ist. Aber wenn der Socket nicht ferngeschlossen, durch ein Signal unterbrochen oder in einem Fehlerzustand ist, wird er blockiert, bis mindestens 1 Byte verfügbar ist.

Mit anderen Worten, wenn Sie für 15 Bytes fragen, aber nur 5 zur Verfügung stehen, wird recv zurückkehren 5.

Anwendungsentwickler sollten nicht darauf verlassen, wie die Daten gesendet wurde oder gebaut und behandeln ihre Fassung als fähig ist Zurückgeben eines Teildatenstroms zu jeder Zeit. (Oder wie ich anderen erzähle, schreibe deinen Code so, als wäre es möglich, dass recv nur jeweils 1 Byte zurückgibt).

Eine allgemeine Schleife zum Empfangen von Daten sieht oft wie folgt aus. Beachten Sie die Zeigermathematik, die im Recv-Aufruf unter buffer erfolgt ist.

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

Die Ausnahme von dieser Regel, die ich kenne, ist der MSG_WAITALL Flag basierte als letzten Parameter zu recv. Das wird den Socket (Block) halten, bis Sie alle Bytes erhalten, die von dem length Parameter angefordert werden, der an recv übergeben wird (oder bis Fehler oder Socket geschlossen werden).

Vom man page for recv:

MSG_WAITALL (seit Linux 2.2) Dieses Flag fordert, dass der Betrieb Block bis zur vollständigen Anforderung erfüllt ist. Jedoch kann der Anruf Rückkehr weniger Daten als angefordert, wenn ein Signal aufgefangen wird, ein Fehler oder Trennen auftritt, oder die nächsten Daten empfangen werden soll, aus einem anderen Typ als die

Die Tatsache

zurückgegeben, dass Sie werden sogar gefragt, diese Frage bringt Sie in eine Liga über den meisten anderen, die Sockets neu sind. In der realen Welt gibt es so viel fehlerhaften Code, da die meisten Entwickler den Fall nicht behandelt haben, dass etwas weniger als erwartet zurückgegeben wird.

+0

Vielen Dank. Also, gibt es kein Entkommen vor der Hässlichkeit und Beschwerlichkeit meines zweiten Schnipsel? – gaazkam

+1

'MSG_WAITALL' könnte Ihre Bedürfnisse erfüllen. Es gibt einen Fehler in Ihrem Code-Schnipsel (Sie haben den Mauszeiger nicht auf "buff" in Ihrer Frage angewendet). Siehe das von mir bereitgestellte Codebeispiel. – selbie

Verwandte Themen