Hallo!
Ich habe eine kleines Programm, das Daten von einem Socket abholt.
Das funktioniert auch soweit ganz gut, sofern ich immer alle Daten mit
der recv-Funktionen abholen kann, wie auch im Empfangspuffer drin
stecken. Ist mein Puffer jedoch noch volltändig abgearbeitet und kann
weniger Bytes aufnehmen, wird meine Receive-Funktion nur einmal
aufgerufen. Die Bytes im Eingangspuffer verbleiben dort, bis auch der
überläuft.
Wie bekomme ich nach dem Aufruf der recv-Funktion heraus, wieviele Bytes
noch im Puffer bzw. zumindest ob noch Daten im Eingangspuffer sind?
Danke & Gruß
Nero
Dafür ist das Protokoll zuständig.
Z.B. beschreibt HTTP-Protokoll die Header-Zeile CONTENT-LENGTH, womit
festgelegt wird, wie viele Bytes im Request-Body stehen.
Ansonsten nutze ich (*nix) epoll oder select mit Timeouts um
herrauszufinden, ob ein Client noch etwas schicken möchte.
Nero schrieb:> Wie bekomme ich nach dem Aufruf der recv-Funktion heraus, wieviele Bytes> noch im Puffer bzw. zumindest ob noch Daten im Eingangspuffer sind?
gar nicht, man ruft es einfach immer wieder auf, wenn man noch mehr
daten haben will.
Patrick-Oliver Scheinert schrieb:> Z.B. beschreibt HTTP-Protokoll die Header-Zeile CONTENT-LENGTH, womit> festgelegt wird, wie viele Bytes im Request-Body stehen.
Da werden geräte Äpfel mit Birnen verglichen.
Um einen HTTP-header parsen zu können müsste auch dieser erstmal
volltändig empfangen werden. Wenn ich allerdings von dem auch nur die
ersten 80 Bytes empfange, komme ich auch nicht weiter.
Peter II schrieb:> gar nicht, man ruft es einfach immer wieder auf, wenn man noch mehr> daten haben will.
Pollen!? Unschön!
Nero schrieb:>> gar nicht, man ruft es einfach immer wieder auf, wenn man noch mehr>> daten haben will.>> Pollen!? Unschön!
nein nicht pollen. read blockiert und pollt nicht. Man kann auch nicht
blockierend arbeiten dann muss man mit select oder poll arbeiten - auch
das ist aber kein pollen.
recv() liefert ja zurück wieviele bytes gelesen wurden. Man muss also in
einer Schleife solange lesen bis man die Sollanzahl bekommen hat. Wenn
man 1000 Bytes erwartet kann man ausserdem nicht davon ausgehen das man
die sofort am Stück bekommt, die können auch in kleineren Häppchen
eintreffen. Ausserdem kann recv() die Anzahl 0 zurückliefern was dann
bedeutet das der Gegner nicht mehr mitspielen will und die Verbindung
beendet hat. recv() kann man bei den meisten OS auf blockierend oder
nicht blockierend schalten, sollte man auch berücksichtigen.
Als nächste Hürde kann man auch nicht davon ausgehen das eine Verbindung
immer ordentlich geschlossen wird, wenn der Sender sich irgendwo
abklemmt kriegt der Empfänger das nicht mit wenn man nicht einen
zyklischen Watchdog in die Übertragung einbaut.
Über was für einen Socket reden wir? TCP?
Bei TCP kannman sich nie verlassen mit einem Call alle Daten zu bekommen
die auf der anderen Seite reingesteckt wurden.
Da die darunter liegenden Schichten versuchen die Übertragung zu
optimieren, können immer erst mal Teile der Daten ankommen.
Du solltest also
1. Wissen wie viele Bytes übertragen werden. Dafür ist dein eigenes
Protokoll zuständig. Am besten du sendest zu Beginn die Anzahl der
folgenden Datenbytes
2. Solange receive aufrufen, bis alle Daten da sind
3. immer den returncode von receive prüfen, denn dort steht drin wie
viele Bytes empfangen wurden.
Man kann sich NIE darauf verlassen.
Mir ist es einmal passiert daß selbst beim receive eines 2 Byte Wertes
beim ersten Aufruf nur ein Byte empfangen wurde. Das Problem konnte
natürlich nur beim Kunden nachgestellt werden und hat uns viel
Kopfzerbrechen bereitet bis der Fehler gefunden wurde.
Nero:
> Da werden geräte Äpfel mit Birnen verglichen.> Um einen HTTP-header parsen zu können müsste auch dieser erstmal> volltändig empfangen werden. Wenn ich allerdings von dem auch nur die> ersten 80 Bytes empfange, komme ich auch nicht weiter.
Dazu habe ich folgendes geschrieben:
> Dafür ist das Protokoll zuständig.
Im Beispiel HTTP weiß man dank des Protokolls (siehe RFC), wie die erste
Zeile auszusehen hat und wie der HTTP-Header beendet wird.
Also lese ich solange, wie es das Protokoll verlangt.
So schwer ist das gar nicht und wie gesagt, es ist ein Beispiel!
Wenn man nicht weiß wieviele Daten gesendet werden, kann man nur endlos
lesen.
Dazu blockiert recv. Will man das nicht, nutzt man select, poll, epoll
... oder arbeitet mit nicht blockierenden Sockets.
Patrick-Oliver Scheinert schrieb:> Dazu habe ich folgendes geschrieben:>> Dafür ist das Protokoll zuständig.> Im Beispiel HTTP weiß man dank des Protokolls (siehe RFC), wie die erste> Zeile auszusehen hat und wie der HTTP-Header beendet wird.
Das ist völlig richtig.
Allerdings zeigt die Frage, daß der TO noch nicht so viel Erfahrung hat
und sehr wahrscheinlich kein so komplexes Protokoll wie HTTP
implementieren will. Entweder er weiss, daß seine Gegenstelle immer n
Bytes sendet, oder er soll in den ersten beiden Bytes die Länge der
Daten übertragen.
Dann aber bitte auf allen Systemen beachten welches Byte eines 2 Byte
Werts zuerst gesendet wird.
Am besten net-byte-order nutzen (Big endian) also erst das höherwertige
Byte übertragen.
Udo Schmitt schrieb:> Allerdings zeigt die Frage, daß der TO noch nicht so viel Erfahrung hat
Richtig erkannt! ;)
Nun aber zu den ganzen Antworten:
Wissen wieviel Bytes kommen, egal wie das in einem Protokoll detektiert
sind doch alles höhere Schichten?!?
Es geht darum, dass der Socket meldet "Daten verfügbar".
Diese liegen im Eingangspuffer des Sockets. - Verwaltet vom
Betriebssystem.
Wie viele Bytes empfangen wurden weiß ich (noch) nicht. Annahme es sind
100 Bytes.
"Mein" Empfangspuffer (CHAR ARRAY) hat auch nur eine begrenzte Größe,
sagen wir 150 Bytes.
Jetzt ist eine vorangegangene Nachricht noch nicht abgearbeitet und
belegt 90 Bytes vom Puffer. Ich hab noch 60 frei.
Beim Aufruf der recv-Funmktion kann ich sagen, dass er bitte nur "60"
Bytes einlesen soll, was mir beim Rückgabewert auch mitgeteilt wird.
Von den 40 Rest weiß ich aber nichts ...
und da der Receive-Event abgearbeitet ist, kommt er auch nur ein mal.
Nero schrieb:> Von den 40 Rest weiß ich aber nichts ...> und da der Receive-Event abgearbeitet ist, kommt er auch nur ein mal.
Sag doch erst mal woher du das Receive-Event hast. Im normales C gibt es
soetwas eigentlich nicht. Dort verwenden man select, poll, WaitForObject
usw und diese Melden einfach wieder das immer noch Daten da sind.
Nero schrieb:> Von den 40 Rest weiß ich aber nichts ...
Kann es sein, dass WSAGetLastError() WSAEMSGSIZE dabei zurück gibt, also
sich noch Daten im Buffer befinden???
Quelle
:http://msdn.microsoft.com/de-de/library/windows/desktop/ms740121(v=vs.85).aspxNero schrieb:> Wie bekomme ich nach dem Aufruf der recv-Funktion heraus, wieviele Bytes> noch im Puffer bzw. zumindest ob noch Daten im Eingangspuffer sind?
Iwie kann man wohl rausfinden wie viel im Buffer steht. Das kann sich
aber ganz schnell ändern... Wenn der return-Wert gleich deiner
bereitgestellten Buffergröße ist, dann musst Du noch mal die Funktion
aufrufen. Wenn recv() 0 liefert, dann legen z.Z. keine Daten im Buffer
an.
mar IO schrieb:> Iwie kann man wohl rausfinden wie viel im Buffer steht. Das kann sich> aber ganz schnell ändern... Wenn der return-Wert gleich deiner> bereitgestellten Buffergröße ist, dann musst Du noch mal die Funktion> aufrufen
wenn jetzt aber nichts mehr drin ist, dann blockiert ist.
Man müsste erst mal etwas Quellcode sehen, damit man hier weiter helfen
kann. Ich habe fast die Befürchtung das es um CLI geht.
mar IO schrieb:> Kann es sein, dass WSAGetLastError() WSAEMSGSIZE dabei zurück gibt, also> sich noch Daten im Buffer befinden???
Das betrifft nur UDP. Der Fehler wird geworfen wenn das "Paket" nicht in
den bei recv() als Parameter angegebenen Puffer passt.
----
Die Anzahl der per recv() unmittelbar abrufbaren Bytes kann man mit
ioctlsocket und FIONREAD auslesen.
Wenn man vermeiden will das recv() blockiert, geht man aber eigentlich
eher anders vor: Man kann z.B. select() mit nem geringen timeout nehmen
oder auf WSAAsyncSelect / non-blocking Sockets zurückgreifen.
Oder man lässt es blockieren, aber in einem eigenen Thread.
Peter II schrieb:> wenn jetzt aber nichts mehr drin ist, dann blockiert ist.
Wenn er so eingestellt ist, dann schon...
bluppdidupp schrieb:> mar IO schrieb:>> Kann es sein, dass WSAGetLastError() WSAEMSGSIZE dabei zurück gibt, also>> sich noch Daten im Buffer befinden???>> Das betrifft nur UDP. Der Fehler wird geworfen wenn das "Paket" nicht in> den bei recv() als Parameter angegebenen Puffer passt.
Ich habe jetzt kurz die Doku dazu überflogen und da steht "als Beispiel
UDP". Weil ich es selber ned weiß, ist das wirklich so, dass das nur
bei UDP funktioniert?
Neben select() kann man auch WSAPoll() benützten, soweit ich das in
Erinnerung habe...
Was auch eine schöne Methode ist, sich einen Callback einzurichten, der
immer aufgerufen wird, wenn neue Daten anstehen.