2010-02-10 3 views
8

Ich habe ein Problem zu verstehen, was recv()/recvfrom() von einem nicht blockig UDP-Socket zurückkehrt.nicht-blockierende Udp-Socket-Programmierung in C: Was bekomme ich?

Etwas präziser und im Vergleich zu TCP (bitte korrigieren Sie mich, wenn ich falsch):

  • Eine blockierenden Socket (TCP oder UDP) nicht von einem recv() zurückgeben, bis sind einige Daten im Puffer. Dies könnte eine bestimmte Anzahl von Bytes (TCP) oder ein vollständiges Datagramm (UDP) sein.

  • Ein nicht blockierender TCP-Socket gibt entweder EWOULDBLOCK (linux)/WSAEWOULDBLOCK (Windows) oder die Bytes zurück, die sich derzeit im Puffer befinden. Da TCP-Daten ein Stream sind, spielt es keine Rolle, wie viele Bytes zurückgegeben werden.

Nun ist die Frage:

  • Eine nicht-blockierende UDP-Sockets auch WOULDBLOCK zurückgibt (Linux)/WSAEWOULDBLOCK (Fenster), wenn es keine Daten verfügbar sind. Aber wenn Daten verfügbar sind, gibt ein nicht blockierender UDP-Socket nur ein paar Bytes zurück, was bedeuten kann, dass Sie nur die Hälfte eines Datagramms bekommen ODER liefert ein UDP-Socket immer vollständige Datagramme?

Edit:

Was ich mit „Hälfte eines Datagramms“ bedeuten, ist: Was passiert, wenn ich recv() aufrufen, in genau der Moment, wenn die Buchse zur Zeit ein Datagramm empfängt. In diesem Moment befinden sich einige Bytes im Puffer, aber das Datagramm ist noch nicht vollständig.

Ihre Erklärungen und Kommentare sind willkommen. Vielen Dank!

Antwort

8

Endlich eine Entschuldigung, meine Stevens-Bücher aus meinen alten Bürokästen auszugraben.

Wenn der Puffer groß genug ist, geben die Berkeley-Standardsteckplätze recv() und niemals ein Teildatagramm zurück. Das Datagramm steht der Anwendung erst zur Verfügung, wenn der Kernel das Datagramm vollständig empfangen und wieder zusammengesetzt hat.

Interessanterweise, und dies ist nicht viel (welche?) Ein Problem heute, andere Netzwerk-Programmierschnittstelle auf dem Verhalten nicht einverstanden ist, wenn die vorgesehenen Puffer zu klein sind:

Die traditionelle Berkeley-Version der Sockets-API schneidet das Datagramm ab und löscht alle überflüssigen Daten. Ob die Anwendung benachrichtigt wird, hängt von der Version ab. (4.3BSD Reno und später können der Anwendung mitteilen, dass das Datagramm abgeschnitten wurde.)

Die Socket-API unter SVR4 (einschließlich Solaris 2.x) schneidet das Datagramm nicht ab. Überzählige Daten werden bei nachfolgenden Lesevorgängen zurückgegeben. Die Anwendung wird nicht benachrichtigt, dass mehrere Lesevorgänge von einem einzelnen UDP-Datagramm erfüllt werden.

Die TLI-API verwirft die Daten nicht. Stattdessen wird ein Flag zurückgegeben, das anzeigt, dass mehr Daten verfügbar sind, und nachfolgende Lesevorgänge durch die Anwendung geben den Rest des Datagramms zurück.

(Stevens, TCP/IP Illustrated, Band 1, Seite 160)

+0

Es scheint, als ob es möglich ist, ein MSG_TRUNC-Flag an 'recvmsg' in Linux zu übergeben und zu empfangen. Dokumentiert in der Manpage 'recv (2)'. In anderen Worten, vielleicht lese ich falsch, aber ich kann nur das Verwerfungsverhalten finden, das in der Manpage für 'socket (2)' dokumentiert ist, das es nur für 'SOCK_SEQPACKET' Sockets erwähnt. Ich habe diese nie persönlich benutzt. –

+0

'MSG_TRUNC' als Argument zu' recv (2) 'ist kein Standard. Es ist weder auf FreeBSD noch auf Mac OS X verfügbar (die Systeme, auf die ich momentan zugreifen kann; wahrscheinlich auch für andere). 'MSG_TRUNC' ist unter Linux, FreeBSD und Mac OS X im' flags'-Member des 'struct msghdr' verfügbar, der an' recvmsg (2) 'übergeben wird. In jedem Fall wird das Datagramm abgeschnitten, wenn der übergebene Puffer nicht groß genug ist, sogar mit 'recv (2)' auf Linux. Der Aufrufer muss den Rückgabewert überprüfen und mit der Puffergröße vergleichen, wenn dort "MSG_TRUNC" verwendet wird. Es wird wissen, Daten sind verloren, aber es ist immer noch verloren. –

+0

Danke! Dies bedeutet, UDP ist ** wirklich ** Paket orientiert ... – Uwe

0

Ich glaube, Sie erhalten genau ein oder null Datagramme. Aber das kann ich in diesem Moment nicht bestätigen. Vielleicht könnte jemand anderes eine gute Referenz geben?

Edit: Ich bin mir ziemlich sicher, dass Sie nicht ein halbes Datagramm erhalten können. Entweder ist das Datagramm im Puffer angekommen oder nicht.

+0

Ich glaube, das ist richtig. Sie können auch einen Fehler erhalten, wenn er nicht in den von Ihnen bereitgestellten Puffer passt oder wenn Ihre mbufs nicht groß genug sind (manchmal mit großen fragmentierten Datagrammen). –

+0

mbufs sind eine BSD-Kernel-Datenstruktur. Sie sind dem Benutzerland nicht ausgesetzt. –

1

Ja, UDP gibt nur zurück, welche Daten in diesem einen Datagramm übertragen wurden. UDP ist nicht stream-orientiert wie TCP. Datagramme sind diskrete Übertragungen und sind in keiner Weise wirklich an andere Datagramme gebunden. Aus diesem Grund ist die Socket-Option für TCP SOCK_STREAM.

Die helle Seite von diesem ist, dass Sie ein Gefühl der getrennten Übermittlungen erhalten können, die mit TCP nicht wirklich einfach sind.

+0

Danke an euch beide. Ich kenne den Unterschied zwischen stromorientiertem TCP (SOCK_STREAM) und paketorientiertem UDP (SOCK_DGRAM). Ich war mir nur nicht sicher, ob ein nicht blockierender UDP recv() auch paketorientiert wäre. Um die Hilfeseite recv() zu zitieren: ... Wenn am Socket keine Nachrichten verfügbar sind, warten die Empfangsaufrufe auf eine Nachricht, es sei denn, der Socket ist nicht blockierend (siehe fcntl (2)), in diesem Fall Der Wert -1 wird zurückgegeben und die externe Variable errno wird auf EAGAIN oder EWOULDBLOCK gesetzt. ** Die Empfangsaufrufe geben normalerweise alle verfügbaren Daten zurück. ** ... – Uwe