Benutzer mit den meisten Antworten
Socketverbindung wird unerwartet unterbrochen

Frage
-
Hallo,
ich habe mich ein wenig mit Socketverbindungen beschäftigt und ein kleines Testprogramm geschrieben, welches Dateien in Blöcken überträgt. Im LAN funzt das auch einwandfrei. Wenn ich jedoch die Verbindung über das Internet zu einem lokalen Rechner herstelle (via DynDns), bricht die Übertragung/Verbindung nach 1-4 Datenblöcken einfach ab. Einen Fehler in meinem Code vermutend, habe ich einen Test mit dem Programmbeispiel von http://dzaebel.net/TcpClientServer.htm gemacht. Hier der identische Effekt, im LAN alles toll, der String wird beliebig oft übertragen, via Internet ist nach einer Übertragung Schluss und der Server kann kein ACK mehr senden weil die Verbindung abgebrochen ist.
Woran kann das liegen? Bin für jeden Tipp dankbar!
Schöne Grüße,
Klaus
PS: Kommunikation lief über Port 44999.
No Brain - No Pain
- Bearbeitet Klaus Mayer Donnerstag, 29. Dezember 2011 18:58
Antworten
-
Klaus,
nur zur Sicherheit,
asynch-sockets lösen (wie etwa Threads) vielleicht Probleme der blockierenden IOs (GUI 'eingefroren'),
keinesfalls jedoch die weitgehend zufällige Fragmentierung des Socket-Streams...Beachte: Verwechsle im folgenden nicht die TCP-'Fragmente' mit deinen eigenen 'Segmenten'!
Vielleicht verdeutlicht folgender Pseudo-Code die Fragmentierung:
stell dir zwei scheinbar 'atomare' Send-Operationen zweier 'Datenblöcke' (etwa 2 deiner Segmente) vor:
Socket.Send("ABCDE")
Socket.Send("FGHIJKL")bei TCP-Stream ist empfangsseitig keine Art von 'Datenblock' mehr gewährleistet,
sondern es können durchaus viele Fragmente (bzw auch 'Fusionen') ankommen, alles unvorhersehbar.
also man könnte nur zB beobachten:
1. Socket.Receive() ergibt "ABC"
2. Socket.Receive() ergibt "DEFG"
3. Socket.Receive() ergibt "HI"
4. Socket.Receive() ergibt "JKL"es ist also kein logischer 'Anfang' bzw 'Ende' mehr feststellbar!
(dies ist by-design in TCP, auch unter zB C++ und Apple/Unix ua; nur die korrekte Reihenfolge der Fragmente ist garantiert)
Datenblöcke musst du also mit eigenem Code
(zB typisch durch Einfügung eines Längenpräfixes oder Delimiter, Vorsicht dabei, Mehrbyte- Längenpräfixe/Delimiter könnten selber auch wieder fragmentiert sein!)
und Schleife selber erkennen / wieder zusammenbauen.
Zum Vorgehen ist es manchmal besser, wenn du zuerst mal die Protokoll-Frage (deine 'Segmente', inkl genannte Fragmentierung) mittels blocking Sockets in zwei (Srv+Clt) einfachen Console-Apps löst, dann per Stresstest (zB via unterscheidlich schnellen Internet-Vrb) austestest.
Als zweites dann Umstellung auf Asynch-Sockets und wieder Tests;
als Drittes dann Einbindung in eine GUI-App (wo dann meist noch die Invoke-InvokeRequired Problematik auftreten wird).- Als Antwort markiert Klaus Mayer Samstag, 31. Dezember 2011 18:57
-
Hallo Klaus,
ergänzend zu Thomas Antwort:
Sleep und Flags haben dort nichts verloren, für solche Aufgaben gibt es Wait Handles.Schau Dir mal an C# in a Nutshell - Chapter 23: Asynchronous Methods
Auch da fehlt noch so ziemlich alles für ein funktionales Programm -
aber die Konzepte dort solltest Du übernehmen können
(fürs Verstehen sinnvollerweise die Variante ohne TPL Tasks).Um ein Protokoll zu entwerfen, ist es besser, wie Thomas schon betont,
auf die zusätzliche Komplexität von asychronen Methoden zu verzichten.Gruß Elmar
- Als Antwort markiert Klaus Mayer Samstag, 31. Dezember 2011 18:57
Alle Antworten
-
Hallo Klaus
ich denke mal das verlinkte, üble Fremd-Beispiel leidet an den gleichen, üblichen Fehlern wie dein Code. Und zwar ist es fast immer die Empfangsseite (recv), die völlig an den TCP-Prinzipien vorbei programmiert ist. Dies klappt evtl mal lokal gerade so, aber via Internet muss man die Spielregeln einhalten.
Du sprichst zB etwa von 'Datenblöcken', obwohl TCP gar keine solche kennt, sondern nur einen Byte-Strom darstellt, der völlig beliebig / anders fragmentiert/fusioniert sein kann (als etwa per Send abgeschickt wurde).
Da musst du deine eigene Block-Logik definieren.
Und falls es sich um Text-Blöcke (Strings) mit einem bestimmten Encoding (zB UTF-8, und andere Multi-Byte Codes) handelt, muss das splitting mitten in einer Codesequenz auch speziell behandelt werden (vom Bsp missachtet, nur im Kommentar angesprochen).Weiter, das 'einbetten' von Tcp-Sockets direkt in eine WinForm mag für eine minimal-Demo mal genügen, aber nicht für ernsthaften Code. Da ist etwa Threading oder Async-Sockets gefragt.
Mein Tipp: vergiss unbedingt das verlinkte Beispiel (wie auch diverse auf MSDN, die vereinfachen halt zu stark). -
>das verlinkte, üble Fremd-Beispiel
:-))
Hallo Thomas,
mit Datenblöcken meinte ich, das mein Prog z. Bsp. eine Datei splittet und diese einzelnen Segmente dann verschlüsselt überträgt. Nach jedem Segment entschlüsselt der Server selbiges, liest aus dem Header Protokollinformationen und die Prüfsumme vom Client, bildet selbst eine Prüfsumme und sendet das Vergleichsergebnis an den Clienten zurück bis die Datei vollständig übertragen wurde. Das Ganze laüft auch über ein eigen definiertes Protokoll ab und klappt einwandfrei im LAN und eben nur ein klein wenig (ein Segment) über das Internet.
Der Server arbeitet auch über Threads. Entscheidend ist, so glaube ich, Dein Hinweis auf asynchrone Sockets und ich werde mich mal daran machen, das Verfahren dahingehend zu ändern. Hast Du eventuell ein leicht verständliches Beispiel dafür?
Danke! und schöne Grüße,
Klaus
No Brain - No Pain -
Klaus,
nur zur Sicherheit,
asynch-sockets lösen (wie etwa Threads) vielleicht Probleme der blockierenden IOs (GUI 'eingefroren'),
keinesfalls jedoch die weitgehend zufällige Fragmentierung des Socket-Streams...Beachte: Verwechsle im folgenden nicht die TCP-'Fragmente' mit deinen eigenen 'Segmenten'!
Vielleicht verdeutlicht folgender Pseudo-Code die Fragmentierung:
stell dir zwei scheinbar 'atomare' Send-Operationen zweier 'Datenblöcke' (etwa 2 deiner Segmente) vor:
Socket.Send("ABCDE")
Socket.Send("FGHIJKL")bei TCP-Stream ist empfangsseitig keine Art von 'Datenblock' mehr gewährleistet,
sondern es können durchaus viele Fragmente (bzw auch 'Fusionen') ankommen, alles unvorhersehbar.
also man könnte nur zB beobachten:
1. Socket.Receive() ergibt "ABC"
2. Socket.Receive() ergibt "DEFG"
3. Socket.Receive() ergibt "HI"
4. Socket.Receive() ergibt "JKL"es ist also kein logischer 'Anfang' bzw 'Ende' mehr feststellbar!
(dies ist by-design in TCP, auch unter zB C++ und Apple/Unix ua; nur die korrekte Reihenfolge der Fragmente ist garantiert)
Datenblöcke musst du also mit eigenem Code
(zB typisch durch Einfügung eines Längenpräfixes oder Delimiter, Vorsicht dabei, Mehrbyte- Längenpräfixe/Delimiter könnten selber auch wieder fragmentiert sein!)
und Schleife selber erkennen / wieder zusammenbauen.
Zum Vorgehen ist es manchmal besser, wenn du zuerst mal die Protokoll-Frage (deine 'Segmente', inkl genannte Fragmentierung) mittels blocking Sockets in zwei (Srv+Clt) einfachen Console-Apps löst, dann per Stresstest (zB via unterscheidlich schnellen Internet-Vrb) austestest.
Als zweites dann Umstellung auf Asynch-Sockets und wieder Tests;
als Drittes dann Einbindung in eine GUI-App (wo dann meist noch die Invoke-InvokeRequired Problematik auftreten wird).- Als Antwort markiert Klaus Mayer Samstag, 31. Dezember 2011 18:57
-
Also irgendwie bekomme ich nicht so recht 'nen Fuß in die Tür. Hab den Code jetzt geändert mit dem Ziel, das der Server erstmal nur irgendwas empfängt, ohne eine Rückmeldung an den Client abzugeben.
Melde("Client-Verbindung akzeptiert von: " + client.Client.RemoteEndPoint.ToString()); byte[] buffer; buffer = new byte[maxBuffer]; while (!receiveDone) { Thread.Sleep(1); ReceiveData(client, buffer); buffer = new byte[maxBuffer]; receiveDone = false; } } void ReceiveData(TcpClient tcp, byte[] data) { try { NetworkStream ns = tcp.GetStream(); ns.BeginRead(data, 0, data.Length, myReadCallBack, ns); while (receiveDone == false) { Thread.Sleep(100); } UTF8Encoding encoding = new UTF8Encoding(); string empfangen = encoding.GetString(data, 0, data.Length); Melde(empfangen); } catch (Exception e) { Melde(e.Message.ToString()); } } public void myReadCallBack(IAsyncResult ar) { try { NetworkStream ns = (NetworkStream)ar.AsyncState; ns.EndRead(ar); receiveDone = true; } catch (Exception e) { Melde(e.Message.ToString()); } }
Und nach wie vor ist das Resultat, das bei einer Verbindung über das INet der Server einmal empfängt und sich anschliessend aufhängt und die nächste Sendung nicht mehr akzeptiert. Wo ist mein Denkfehler?Für Eure Hilfe vielmals dankend,Klaus
No Brain - No Pain- Bearbeitet Klaus Mayer Samstag, 31. Dezember 2011 08:12
-
... bei einer Verbindung über das INet der Server einmal empfängt und sich anschliessend aufhängt
Klaus,
nur damit es keine Verwechslung gibt, dein 'Aufhänger' darf man nicht in Verbindung mit meiner vorherigen Aussage:
"asynch-sockets lösen (wie etwa Threads) vielleicht Probleme der blockierenden IOs (GUI 'eingefroren')"bringen!
Bei deinem Fall gibt es offenbar schon viel grundlegendere Ablauf-Probleme - dagegen die Async-Sockets lösen evtl später mal eine andere Problematik (die dir wohl noch bevorsteht).Alleine mit deinem (jetzt künstlich noch komplizierteren, Timing-sensitiveren) Code für den Server ist es schwierig, die Ursache zu finden!
(insbesondere falls der Client immer noch auf Basis jenes untauglichen Fremd-Bsp ist)
Ich kann dir nur nochmals empfehlen, erstelle erst mal simple Console Apps (Clt+Srv) mit den viel einfacher beherrschbaren, blockierenden IOs und arbeite die elementaren Protokollablauf-Fragen durch. -
Hallo Klaus,
ergänzend zu Thomas Antwort:
Sleep und Flags haben dort nichts verloren, für solche Aufgaben gibt es Wait Handles.Schau Dir mal an C# in a Nutshell - Chapter 23: Asynchronous Methods
Auch da fehlt noch so ziemlich alles für ein funktionales Programm -
aber die Konzepte dort solltest Du übernehmen können
(fürs Verstehen sinnvollerweise die Variante ohne TPL Tasks).Um ein Protokoll zu entwerfen, ist es besser, wie Thomas schon betont,
auf die zusätzliche Komplexität von asychronen Methoden zu verzichten.Gruß Elmar
- Als Antwort markiert Klaus Mayer Samstag, 31. Dezember 2011 18:57
-
Ich danke Euch für Eure Hinweise und Tipps. Die Thematik ist ja doch komplexer als ich annahm :-) Aber mit Euren Hinweisen werde ich es wohl hinbekommen.
Allen einen guten Rutsch und ein gesundens neues Jahr.
Schöne Grüße,
Klaus
No Brain - No Pain