Spezialisten gefragt: Windows Socket: Empfangsschleife verschluckt Daten.
-
Ok.
Freund.
Es wurde dir mehrfach gesagt was du falsch machst, und du ignorierst es einfach. Was sollen wir dir jetzt noch sagen?Falls du nicht weisst was ich meine: recv() gibt zurück wieviel Bytes empfangen wurde, aber das hat nichts damit zu tun was auf der send Seite an send() übergeben wurde!
Wenn du send(..., 10000) machst kann es auf der recv Seite passieren dass recv erstmal 1400 Byte oder so empfängt, und dann erst den Rest. Anders gesagt: recv gibt dir die Daten in Häppchen zurück so wie sie gerade daherkommen und nicht wie sie gesendet wurden.Wenn du das berücksichtigst ist der von dir gepostete Code einfach nur falsch. Daher kann man auch nicht erwarten dass er funktioniert.
Behebe erstmal DAS Problem (auf beiden Seiten, Server UND Client!) und vergiss bis dahin die Sache mit den "lauter Nullen".
-
hustbaer schrieb:
Ok.
Freund.
Es wurde dir mehrfach gesagt was du falsch machst, und du ignorierst es einfach. Was sollen wir dir jetzt noch sagen?Ok.
Ich es nicht ignoriert, sondern einfach nicht verstanden.Ich soll also den Rückgabewert von recv auswerten.
Doch verstehe ich nicht was mir das bringen soll. Was erfahre ich dadurch?
Ich erfahre doch, wieviel bytes recv aus dem Stream gelesen hat.Beispiel:
Also der Server sendet ein Stream der 4095 bytes groß ist.
das nenne ich mal ein Packet.
Aus meinem ersten Beispiel erkennt man, daß ich unterschiedliche Packete mit unterschiedlich vielen belegten Bytes bekomme.
Nehmen wir mal an, im 1. Packet sind effektiv 300bytes drin, der Rest bis 4095 wird vom Server mit 0 aufgefüllt.
Jetzt empfange ich das erste Packet mit recv.
Die Frage ist nun: Empfange ich 4095 Bytes oder 300???
Empfange ich 300, weiß ich ja nicht ob das alle Daten waren, da ich immer unterschiedlich lange Daten kriege.Aber ok das kann ich mal genau austesten.
Im Falle ich bekomme von recv nu die 300 zurückgeliefert, bringt mit die Auswertung gar nichts, da ich im Vorfeld ja nicht weiß ob es 300 oder 3000 bytes in einem Packet sind, die mir der Server liefert.Würde ich immer 4095 empfangen könnte ich den Rückgabewert auswerten und mit dem nächsten Schleifendurchlauf addieren bis ich 4095 zusammen habe.
Dann wüsste ich auch, daß ich ein Packet vom Server bekommen habe.Der Server hingegen weiß wieviel bytes er senden soll und überprüft dieses auch mit dem Rückgabewert von send().
Ist es das was ich tun soll?
Ich hoffe ich konnte erläutern wo mein Denk-Knoten festsitzt...Danke für Eure Hilfe und schönen Sonntag noch.
Mondmann
-
Wenn Du wissen willst, wie viele Daten der Server geschickt hat, hast Du zwei Möglichkeiten:
1. Du wartest eine gewisse Zeit bis nix mehr kommt (sehr unzuverlässig)
2. Du änderst Dein Protokoll, damit Du erkennen kannst, ob noch Daten kommenDas 2. würde ich Dir empfehlen.
Wenn dabei schon vor dem Senden feststeht, wieviel gesendet werden soll, so schicke doch diese Länge zuerst (zB. immer 4 Bytes Prefix mit der Länge). Und dann die 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