Socket-Verhalten
-
zuckerlie schrieb:
Was gäbe es denn z.B. für Möglichkeiten bzw. Alternativen?
Üblicherweise hängt man die empfangenen Bytes an einen Puffer an. Nach jedem Empfang prüft man, ob ein vollständiges Paket im Puffer ist, nimmt es raus und bearbeitet es. Das macht man, bis kein vollständiges Paket mehr im Puffer ist.
-
Such mal im WINAPI - Forum, da gibt es diverse Beiträge zu dem 'Problem'. Aber wie schon gesagt wurde:
Wenn Du 10000 Byte Daten erwartest, dann ist nicht garantiert, dass Du die mit einem einzigen recv - Aufruf bekommst. Es kann sein, deshalb läuft es sicher ab und zu, es kann aber auch sein, dass Du dreimal recv aufrufen musst und dann zB 2000, 3000 und 5000 Byte bekommst.
Diese Eventualität musst Du halt managen.
-
Du brauchst ein Protokoll, dass irgendwie die Telegrammlänge spezifiziert. Ausserdem brauchst du eine Art FIFO, wo hinten Daten eingefügt und vorn Daten entnommen werden. Im Pseudocode könnte das so aussehen:
while( connected ) { count = receive( ReceiveBuffer ); if( count > 0 ) { TelegramBuffer.append( ReceiveBuffer ); while( TelegramBuffer.size() >= TelegramHeaderLength ) { TelegramHeader* Hdr = (TelegramHeader*) &TelegramBuffer[0]; if( TelegramBuffer.size() >= Hdr->TelegramLength ) { // Telegramm behandeln TelegramBuffer.erase_front( Hdr->TelegramLength ); } } } }Nach jedem Empfang hängst du die Daten in einen Puffer an. Solange der Puffer genügend Daten für den Telegrammkopf enthält interpretierst du die Daten am Pufferanfang als Telegrammkopf und guckst nach, wie lang das Telegramm ist. Wenn der Puffer genügend Daten für das Telegramm enthält entnimmst du das komplette Telegramm dem Puffer. Das funktioniert für Telegramme unterschiedlicher Länge, wenn du eine feste Telegrammlänge von 42 Byte hast wird´s natürlich wesentlich einfacher, weil du immer nur gucken musst, ob 42 Bytes im Puffer stehen.
Wie würdest du das Problem denn lösen, wenn dein Telegramm 42 Byte lang ist, aber bei jedem Empfang immer nur 11 Byte gelesen werden?
-
iRecv = recv(this->sock, recvbuf , Nutzdatenlänge, 0) while(iRecv < Nutzdatenlänge) { iRecv += recv(this->sock, recvbuf+iRecv, Nutzdatenlänge, 0) };Da ich erst den Puffer komplett füllen möchte, die Frage, ob es auch so gehen würde?!
recvbuf ist ein char* Pointer auf einen vorher allokierten Buffer. Dieser wird um die Anzahl der abgelesenen Bytes inkrementiert, damit die vorhandenen Daten im "recvbuf" nicht überschrieben werden.
-
Vielen Dank erstmal Leute.
Ja, ich weiß, dass durch ein einzelnes recv() nicht alle Daten gelesen werden. Deshalb habe ich es so, wie ganz oben angegeben, in die Schleife(nbedingung) gesetzt( Das habe ich von Codeproject damals übernommen).
//Entschuldigung - wollte keinen extra Post machen.
-
zuckerlie schrieb:
Hallo Leute,
int Socket::RecvHeader(char * headerbuf){ this->SockReturn = 0; while((this->SockReturn = ::recv(this->sock,headerbuf,42,0)) < 42) { ::Sleep(1); }; return this->SockReturn; };Genau da ist der Fehler. Wenn Du weniger als 42 Bytes empfängs, liest Du noch mal 42 Bytes. Und das noch dazu an den Anfang deines Puffers. Bekommst Du beispielsweise 2 Bytes beim ersten Aufruf und 42 beim nächsten, hast Du die Bytes 2-43 (also von 0 an gezählt) in Deinem Puffer, statt 0-41, wie Du es erwartest. Bei den Nutzdaten machst Du das dann korrekt.
Und so ganz nebenbei: ein Sleep ist immer die falsche Lösung. Nimm select oder poll.
-
zuckerlie schrieb:
while((this->iRecv = ::recv(this->sock,recvbuf,NUTZDATENLAENGE,0)) < NUTZDATENLAENGE) { ::Sleep(1); };Angenommen, Du erwartest die von mir weiter oben erwähnten 10000 Bytes, die aber nur portionsweise ankommen. Dann bekommt this->iRecv nacheinander die Werte 2000, 3000 und 5000 - bleibt aber immer kleiner als 10000 und deshalb kommt das Programm nicht aus dieser Schleife heraus.
Und was soll überhaupt das Sleep(1) hier?
-
So, ich melde mich zurück.
Erstmal vielen Dank.
Ich habe schon oft von diesem Problem gelesen, aber nicht gedacht, dass es in diesem Fall zutrifft.
Wie ich bereits sagte, habe ich bei Codeproject gelesen, dass das recv in die Bedingung der While-Schleife gesetzt wurde und somit keine mehrfachen Aufrufe innerhalb der Schleife nötig sind. Das Sleep(1) innerhalb dieser Schleife war um ggf. sonst eine Millisekunde zu warten und dann nochmal zur Bedingung zu gehen.
Ich habe es nun so umgesetzt:
int connectsocket::RecvPayload(char * recvbuf, int iPayload) { this->iReturnPayload = 0; this->iReturnPayload = ::recv(this->sock, recvbuf,iPayload, 0); //recvbuf innerhalb der main: // char * recvbuf = new recvbuf[Payload] while(this->iReturnPayload != iPayload) { this->iReturnPayload += ::recv(this->sock, recvbuf+this->iReturnPayload ,iPayload, 0); }; return this->iReturnPayload; }
-
Falls die Gegenstelle weitersendet, willst Du hier aber nur die für diese Übertragung gewünschte Datenmenge lesen, deshalb:
this->iReturnPayload += ::recv(this->sock, recvbuf+this->iReturnPayload ,iPayload - iReturnPayload, 0)
-
Bitte keine ungarische Notation und mach die ganzen this-> weg.

-
sockets sind schwer... zumindest für mich! und wenn ich mich jetzt nicht täusch, sind in deinem so viele fehler (was passiert bei ::recv rückgaben von 0 bzw -1 was ja kleiner als dein NUTZDATENLAENGE ist?), dass ich dir empfehle eine fertige library zu nehmen.
okay, evtl. wird da auch dann ganz was anderes gemacht, kenn ja ::recv nicht und will dir jetzt nichts unterstellen

@edit warte, das ist windows... da ists statt -1 SOCKET_ERROR, oder

-
Nö, sockets sind eigentlich ziemlich simpel.
@zuckerlie
Für dich gibt es zwei Lösungen: select() oder Threads. (Oder poll(), aber das dürfte Windows nicht haben.) (Du kannst den Socket auch select()en nachdem er verbunden wurde, um zu testen ob du etwas Lesen kannst.)Und ja, du musst natürlich den Rückgabewert von recv() überprüfen bevor du das irgendwo drauf addierst, ist doch klar. Und hör auf Pi.
-
Oh man oh man, vielen Dank Leute.
Ihr habt mir sehr geholfen.
@Belli, da hast du recht. Das habe ich natürlich auch nicht bedacht.
//edit, aber das ändert doch nichts so wirklich, da ich ja sowieso nach einer gewissen menge aufhöre. sollte doch selbst so ohne probleme gehen?!@Cooky, ja der Rückgabewert wird geprüft. Habe ich schon programmiert. Ja, ich nutze schon select() um zu schauben, ob ich lesen kann.
@Pi, spricht was dagegen?
Vielen Dank Euch

-
-
314159265358979 schrieb:
zuckerlie schrieb:
@Pi, spricht was dagegen?
Spricht was dafür?

Übersicht?!
-
xDDDDDD
Spinner.
-
zuckerlie schrieb:
@Belli, da hast du recht. Das habe ich natürlich auch nicht bedacht.
//edit, aber das ändert doch nichts so wirklich, da ich ja sowieso nach einer gewissen menge aufhöre. sollte doch selbst so ohne probleme gehen?!Nein. Stell Dir vor, Du bist mit dem Lesen des Päckchens noch nicht ganz fertig. Sagen wir, Dir fehlen noch 3000 Byte. Nun sendet die Gegenstelle wieder 10000 Byte. Von Deinem Socket sind jetzt insgesamt 13000 Byte lesbar, wovon 3000 Byte noch zu dem aktuellen Päckchen gehören, und auch nicht mit den nächsten 10000 zusammengewürfelt werden dürfen.
Nun kommt Deinthis->iReturnPayload += ::recv(this->sock, recvbuf+this->iReturnPayload ,iPayload, 0);wobei iPayload ja die ganze Zeit den Wert 10000 hat.
Jetzt liest dieser Aufruf von recv möglicherweise mehr als die noch benötigten 3000 Byte, nämlich irgendwas zwischen 1 und 10000, weil Du 10000 (iPayload) als Maximalwert angegeben hast.
Alles klar?Zu allem Überfluss wird jetzt nicht mal mehr die Schleife verlassen, weil Du als Bedingung '!=' drin hast, iReturnPayload jetzt aber größer als iPayload geworden ist ...
-
Oh vielen Dank. So habe ich das gar nicht gesehen.
Wieder mal was gelernt
Danke@Pi, laber nicht so nen Müll,sondern begründe doch mal deine erste Aussage..
-
this-> ist in praktisch allen seinen Fällen komplett redundant. Redundanter Code ist schlecht.
-
zuckerlie schrieb:
@Pi, laber nicht so nen Müll,sondern begründe doch mal deine erste Aussage..
Das ist zu offensichtlich für eine Begründung. Recherchieren kannst du selber.