Spezialisten gefragt: Windows Socket: Empfangsschleife verschluckt Daten.
-
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?
-
Wenn recv einen Fehler meldet machst du den Socket zu, da die Verbindung dann wahrscheinlich bereits "tot" ist. Der Fehlercode (den du mit WSAGetLastError bekommst) sagt dir warum ("connection reset by peer" etc.).
Ausnahmen sind natürlich Fehler wie WSAEWOULDBLOCK (bekommst du bei blocking IO nicht), WSAEMSGSIZE (bekommst du bei TCP nicht), WSAESHUTDOWN (bekommst du nur wenn du shutdown aufgerufen hast - was du dann wissen müsstest) etc.
Alles in Allem kann man sagen: wenn du bei blocking IO einen Fehler bekommst dann hat entweder dein Programm einen Fehler gemacht, oder die Verbindung ist "tot", oder du tust etwas "spezielles" (z.B. shutdown aufrufen oder einen blocking Socket aus mehreren Threads gleichzeitig zu verwenden etc.), wobei du dann aber wissen müsstest was du da tust, und welche Fehler es bei send/recv zur Folge haben kann.
Wenn du unsicher bist lies dir einfach die Liste der möglichen Fehlercodes in der MSDN durch (Funktionen recv und send). Und schreib einen Eintrag in ein Logfile wenn send oder recv einen Fehler zurückliefert, dann kannst du zumindest gucken was passiert.
-
Wenn ich 4095Bytes durchs Netz schicke, wird dieses Paket auf jeden Fall fragementiert, da der MTU ja standesgemäß 1500 ist, oder?
-
Wenn ich 4095Bytes durchs Netz schicke, wird dieses Paket auf jeden Fall fragementiert, da der MTU ja standesgemäß 1500 ist, oder?
Jep. Warum ist das wichtig für dich?