Spezialisten gefragt: Windows Socket: Empfangsschleife verschluckt Daten.
-
Ok, Mondmann, was du anscheinend (denke ich zumindest) nicht verstanden hast:
Wieviel bytes recv auf einmal empfängt ist purer Zufall. Wenn du 4095 Bytes rausschickst kann es sein dass recv nur 100 Bytes empfängt oder nur eins. Wenn du die restlichen Bytes haben willst musst du recv solange wieder aufrufen bis alles angekommen ist was du haben wolltest.Wenn du also "Pakete" verschicken willst, und der Empfänger vorher nicht wissen kann wie gross ein Paket ist, dann wäre wohl die beste Lösung vorher mitzuschicken wie gross es ist.
Zum Empfangen holst du dir dann zuerst einen Block von z.B. 4 Bytes (wenn die die Länge als 4 Byte Integer verschickst), und dann nochmal N Bytes für die Daten.Also das was Jochen Kalmbach vor mir schon beschrieben hat (sehe ich erst jetzt).
-
Ja das stimmt, das hab ich nicht verstanden bisher.
Wir haben uns schon überlegt immer die Menge der Bytes vorauszuschicken, eine Art Header für den Gesamten Kommunikationsprozess.
Ich schätze mal etwas anderes bleibt nicht übrig.
Ich werde morgen nochmal genau prüfen was recv empfängt und wo die Daten geblieben sind.
Es fehlen nämlich auch manchmal ganze "Pakete".Ich denke das Problem liegt auch daran, daß die Software die Daten zu schnell rausballert, ehe ich alle empfangen kann.
Dann läuft der Socket voll und ist blockiert.
Kann sowas sein?
Ist nur ne Theorie...
Wir wissen nicht wirklich was da auf dem Socket passiert nur, daß es nicht wirklich funktioniert.Wir haben schon ne Sendeverzögerung eingebaut, da hats ein bischen besser funktioniert aber das ist eine Sisyphosarbeit.
Ich werde dann morgen berichten...
Mondmann
-
Wenn Du "TCP" verwendest, dann kommen die Daten zuverlässig an. D.h. es geht nix verloren!
-
Hm...es gingen aber schon definitiv Pakete verloren...
-
Wenn der Puffer beim Empfänger voll gelaufen ist, wird der Sender die Sendegeschwindigkeit reduzieren und Pakete die vom Empfänger wegen Platzmangel im Puffer verworfen werden mussten erneut senden -> Es gehen keine Daten verloren.
Aus Winsock-Sicht bekommt man von solchen Sachen allerdings sowieso nichts mit - Das wird alles schön im Hintergrund geregelt.Aus Winsock-Sicht bei blocking-sockets: Entweder Daten sind da (recv() blockiert nicht), es sind keine Daten da (recv() blockiert bis was ankommt), oder die Verbindung ist tot (recv() meldet SOCKET_ERROR)
Die Daten kommen dabei immer in richtiger Reihenfolge an.Es handelt sich hier definitiv um einen Programmierfehler.
Sagen wir mal du haust mit send() raus als Bytes: 12, 6, 8, 14, 26
Je nachdem wie schnell die Verbindung gerade ist, kann es halt sein das erstmal nur 12 ankommt, beim nächsten recv() Aufruf dann 6, bei wieder dem nächsten dann 8, 14 und 26.
Im Prinzip wie ein Stream, recv() hat dabei natürlich keinen Schimmmer wieviele Daten es wohl insgesamt von irgendeinem Sender bekommen wird.Die häufigsten Fehler-Quellen:
- Rückgabewert von recv() wird nicht (richtig) beachtet.
- Rückgabewert von send() wird nicht (richtig) beachtet.
- Die Daten von mehreren recv()-Aufrufen werden nicht richtig zusammengesetzt.
- Das sich ausgedachte Protokoll ist schlicht Murks.Erstaunlicherweise wird in einigen Winsock-Tutorials das Prüfen der Rückgabewerte von send() und recv() nichtmal erwähnt...
-
Danke für Deine Hinweise.
Die Erkenntnis, daß recv nich alles empfängt, sonder willkürlich das was es bekommt ist der Schlüssel.
Ich habe jetzt den Rückgabewert von recv ausgewertet und festgestellt, daß ich normalerweise IMMER 4095 zurück geliefert bekomme.
Selbst wenn in dem PAket nur 20 Bytes belegt sind.Wenn irgendwas anders ist, dann bekomme ich zB.1452 geliefert und das nächstemal 2643. Und DAS sind dann meine nullen. Es ist einfach der Rest vom letzten Packet!.
Diese zwei Werte kommen übrigens recht häufig vor...warum weiß ich auch nicht.
Also was fange ich nun damit an?
Ich prüfe einfach den Rückgabewert von recv.
Sind es 4095 weiß ich, daß ein Paket komplett ist.
Sind es weniger, zähle ich einfach zusammen bis ich 4095 zusammen habe.
Dann ist dieses Paket komplett.Das wäre mal ein erster Schritt, was meint Ihr?
-
Ich würde nicht 4095 bytes senden, wenn da sowiso nur die ersten 20 bytes genutzt sind. Implementiere ein protokoll, in welchem festgelegt wird, wann ein Nachricht anfängt, aufhört (bzw. wie lang sie ist) und wie der Inhalt zu interpretieren ist.
simon
-
Mondmann schrieb:
Die Erkenntnis, daß recv nich alles empfängt, sonder willkürlich das was es bekommt ist der Schlüssel.
recv empfängt schon alles... aber eben nicht alles auf einmal!
Mondmann schrieb:
normalerweise IMMER 4095 zurück geliefert bekomme.
Selbst wenn in dem PAket nur 20 Bytes belegt sind.Nein! Dann hast Du was falsch gemacht oder eben mehrere Pakete gleichzeitig erhalten!
Du musst Dich davon lösen, dass ein "send" aufruf in einem "recv" aufruf mündet!
Mondmann schrieb:
Wenn irgendwas anders ist, dann bekomme ich zB.1452 geliefert und das nächstemal 2643. Und DAS sind dann meine nullen. Es ist einfach der Rest vom letzten Packet!.
Jetzt hast Du es glaube ich verstanden...
Mondmann schrieb:
Ich prüfe einfach den Rückgabewert von recv.
Sind es 4095 weiß ich, daß ein Paket komplett ist.NEIN! Gibt die Länge mit und prüfe dann ob schon alles da ist, oder ob DU schon ein Teil des nächsten Paket empfangen hast!
-
Wir sind gerade dabei, die 4095 vollzuballern bis zum Anschlag.
Aber das ist erst der nächste Schritt.NEIN! Gibt die Länge mit und prüfe dann ob schon alles da ist, oder ob DU schon ein Teil des nächsten Paket empfangen hast!
Warum nein?
Weil es dann sein könnte, daß ich dann ein teil vom Nächsten Paket bekomme?Wenn ich die Länge mitgebe, kann ich mit strlen dann prüfen wieviel Daten bereits zusammengekommen sind.(?)
Denn recv gibt mir ja alles zurück, auch nullen.Naja ganz verstanden habe ich es noch nicht, aber ich denke es wird langsam...
-
Leider gehen doch Pakete verloren.
Ich hatte eben einen Fall bei dem die ersten 4 von 22 gefehlt haben...waren einfach weg...
Der Server hat sie aber rausgeschickt...Es fehlen auch zwischendurch immer wieder 5-6 Pakete hintereinander.
Die Software schickt sie aber auf den Weg.
Nachweislich.
-
sinnlos.
-
Mondmann schrieb:
Wenn irgendwas anders ist, dann bekomme ich zB.1452 geliefert und das nächstemal 2643. Und DAS sind dann meine nullen. Es ist einfach der Rest vom letzten Packet!.
Genau. Deswegen hatte ich anfangs gemeint lies die Doku zu send/recv nochmal, denn da steht das eigentlich drinnen
Diese zwei Werte kommen übrigens recht häufig vor...warum weiß ich auch nicht.
Das liegt an der MTU, also der max. Paketgrösse des verwendeten Netzwerks. Die ist bei Ethernet normalerweise 1500. Die 1452 sind wahrscheinlich das was übrigbleibt wenn man die ganzen Headers wegrechnet.
Also was fange ich nun damit an?
Ich prüfe einfach den Rückgabewert von recv.
Sind es 4095 weiß ich, daß ein Paket komplett ist.
Sind es weniger, zähle ich einfach zusammen bis ich 4095 zusammen habe.
Dann ist dieses Paket komplett.Das wäre mal ein erster Schritt, was meint Ihr?
Das wäre eine Möglichkeit, ja. Oder eben du überträgst gleich die Länge der Nutzdaten vor dem Paket - das wäre effizienter (weniger Nulldaten die du rumsendest).
-
Mondmann schrieb:
Leider gehen doch Pakete verloren.
Du meinst auch nicht Pakete sondern Bytes, denn wie erwähnt gehen TCP-Pakete nicht verloren. Und da keine TCP-Pakete abhanden kommen, kommen auch keine Bytes abhanden. Also ist immer noch nen Fehler in deiner Anwendung
-
ist bestimmt ein Fehler in der TCP/IP Implementierung des Betriebssystem
-
bist du sicher? oder vielleicht doch ein compiler fehler...
-
Es kann eigentlich nur ein Fehler des OS sein. Ist halt Windows! Da braucht man sich nicht zu wundern.
Oder der *Microsoft* Compiler macht mal wieder Fehler! Was ja eigentlich alle paar Minuten vorkommt!
-
Ich habe mal gebastelt und mit dem unten angeführten Code gehts ganz gut.
Man sieht die Schleife für 1. Paket. Diese Schleife ist nochmal in eine For-Schleife verpackt die die anzahl der empfangenen Pakete mitzählt und auswertet.Dises ist hier nicht abgebildet.iGesLen und iSollLen werden nach jedem Durchlaf neu initialisiert.
iGesLen = 0; iSollLen = MAX_REC_BUF; iFehlerCnt = 0; while(iGesLen < MAX_REC_BUF ) { if( strstr(pRecv,STOPSEQUENZ) !=0) { break; } if((iAktLen = recv (s,pRecv, iSollLen,0 )) > 0) { memcpy(InBuffer + iGesLen,pRecv,iAktLen); iGesLen += iAktLen; iSollLen -= iAktLen; } else { if( strstr(pRecv,STOPSEQUENZ) !=0) { break; } iFehlerCnt++; Sleep(10); } if(iFehlerCnt >= 100) { break; } }
Meine Denkfehler bzw. Unwissenheit waren:
1. daß recv immer das empfängt was send() sendet und
2. ging es über meine Vorstellung hinaus, daß man recv auch mal weniger empfangen lassen kann als der max. buffer.
3. daß Pakete verloren gehen, dem ist nicht so.
4. Daß strlen bzw. strcpy ja nur bis zum \0 auslesen und selbst wenn danach noch Daten stehen diese nicht mitgezählt/kopiert werden.
Was in diesem Falle ja durchaus öfters der Fall sein wird.Ich Danke allen die sich Mühe gemacht haben und mir geduldig erklärt und geantwortet haben.
Es ist ein tollen Forum und ich werde hier immer gut geholfen, auch wenns meinem festgefahreren Hirn mal schwieriger wird.Allerdings habe ich nie bezweifelt, daß der Fehler an mir liegt.
Mondmann
-
Zur Info: Ich habe mich bei meinem letzten Satz verschrieben...jetzt stimmts was ich sagen wollte....:-)
-
Bei einem Fehler würde ich sofort abbrechen, der Socket wird sich nicht wieder "erholen" (glaub mir, ich hab nen Server geschrieben mit dem sich täglich 1000e Geräte verbinden und der pro Tag etliche GB an Daten verschickt).
Und was das strstr in der Schleife soll verstehe ich nicht ganz.
Kannst du denn sicherstellen dass STOPSEQUENZ *niemals* mitten in einem Paket vorkommen kann?
-
Hallo hustbaer.
STOPSEQ wird niemals mitten im stream vorkommen, außer es geht etwas schief und es steht noch ein "Rest" im Socket.
Es ist einfach das Ende der Sendung und da breche ich ab, sonst blockiert recv.Ja bei einem Fehler..hmm...ich denke das ist noch ein Problemchen.
Wenn jetzt ein Fehler vorkommt, der Socket aber noch nicht leer ist und ich breche ab..was dann?Dann holt recv bei der nächsten Anfrage auf diesem Sochet erstmal den Rest ab mit dem ich dann nichts anfangen kann.
Also was hilft?
Socket beenden und neu aufbauen?