# Datenpakete zusammenfügen und trennen



## mainfield (26. Okt 2010)

Hallo,

ich habe eine grundlegende Frage zu Sockets (TCP) bzw. Streams. Ich möchte nachdem ich die Verbindung aufgebaut habe, Bytes übertragen (Inhalt einer Datei und zusätzliche Daten). Daher verwende ich BufferedInputStream und BufferedOutputStream. Die zu übertragenden Daten möchte ich in einzelne Pakete/Datensätze aufteilen (für jede Datei ein Datensatz). 

Wenn ich die einzelnen Pakete in einem Programm einfach nacheinander in den OutputStream schreibe (Client), kommen sie aber im anderen Programm (Server) "am Stück" an. Mein Problem ist jetzt, die Daten aus dem InputStream wieder in die einzelnen Pakete zu zerlegen.

Meine erste Idee war, beim Senden nach jedem Paket ein Trennzeichen zu senden. Beim Empfangen wird dann einfach nach diesem gesucht und die Daten entsprechend aufgeteilt. Das Problem ist aber, dass die Nutzdaten, also die Daten innerhalb des Datenpakets u.U. auch das Trennzeichen enthalten. Dann würde ein eigentlich zusammenhängendes Pakte fälschlicherweise in zwei oder mehr aufgeteilt.

Die andere Idee wäre, beim Senden vor jedes Paket die Größe des Pakets, also die Anzahl der Bytes aus denen es besteht, zu schreiben. Wenn dann aber einmal die angegebene Anzahl an Bytes nicht mit der tatsächlichen Anzahl übereinstimmen sollte (z.B. wegen Berechnungsfehler), kommt das ganze Programm aus dem Tritt und es wird an der falschen Stelle nach der Größe des Paketes gesucht.

Gibt es eine einfache Lösung, wie ich einzelne Datensätze in einem InputStream zusammensetzen und danach wieder zuverlässig trennen kann?

Vielen Dank!


----------



## schalentier (26. Okt 2010)

mainfield hat gesagt.:


> Die andere Idee wäre, beim Senden vor jedes Paket die Größe des Pakets, also die Anzahl der Bytes aus denen es besteht, zu schreiben.



Genau so. Da du TCP verwendest, hast du kein Uebertragungsproblem (TCP hat ne Fehlerkorrektur drin). Und Berechnungsfehler kannst du mit nem Unittest ausschliessen.


----------



## mainfield (27. Okt 2010)

Sollte aber einmal die tatsächliche Größe in einem Paket nicht mit der angegebenen übereinstimmen (z.B. weil sich trotz Modul-Test ein Fehler eingeschlichen hat [schließlich lassen sich ja nicht alle Eingabekombinationen testen] oder weil sich mein Programm mit einem fehlerhaften Programm verbindet, das nicht von mir stammt), dann würde keines der folgenden Pakete mehr richtig zerlegt werden (weil immer ein falsche Byte als Länge interpretiert wird). 

Gibt es eine gute Möglichkeit mit der sich so ein Fehler erkennen lässt, so dass sich das Programm nach ein Paar ignorierten Bytes wieder "einfängt"?


----------



## schalentier (27. Okt 2010)

Mh, evtl. koenntest du immer eine feste Anzahl Bytes (zb. 4096) lesen, dann kommt eine Pruefsumme (4 Byte), dann der naechste Block fester Groesse. Aber ob das wirklich sinnvoll ist... wie willst du denn reagieren, wenn die Gegenseite Muell sendet? 

Ich denke es ist unwahrscheinlich, dass nach einigen fehlerhaften Daten ploetzlich wieder korrekte Daten ankommen. Will sagen, entweder es kommen fehlerhafte Daten oder eben nicht. Im Fehlerfall bleibt eigentlich nicht viel mehr, als Verbindung trennen und von vorne anfangen. 

Ansonsten is das ganze ziemlich stark vom konkreten Fall abhaengig, es ist also schwierig hier allgemeingueltige Tipps zu geben ;-)


----------



## mainfield (27. Okt 2010)

> Mh, evtl. koenntest du immer eine feste Anzahl Bytes (zb. 4096) lesen, dann kommt eine Pruefsumme (4 Byte), dann der naechste Block fester Groesse. Aber ob das wirklich sinnvoll ist... wie willst du denn reagieren, wenn die Gegenseite Muell sendet?


Da hast Du wohl recht. Ich dachte erst, dass ich eine Art Fehlermeldung zurück schicke. Aber eine zusätzliche Behandlung würde den Stream natürlich aufblähen. Wenn ich mir die Kosten-Nutzen anschaue, erscheint mir das als wenig sinnvoll.



> Ich denke es ist unwahrscheinlich, dass nach einigen fehlerhaften Daten ploetzlich wieder korrekte Daten ankommen. Will sagen, entweder es kommen fehlerhafte Daten oder eben nicht. Im Fehlerfall bleibt eigentlich nicht viel mehr, als Verbindung trennen und von vorne anfangen.


Gut, dann werde ich einfach zu jedem Datensatz die Länge mit senden. Wenn sich die Gegenseite nicht an das Schema hält, dann hat sie eben Pech gehabt. 

Meinst Du, es hat Sinn wenn ich an das Ende jedes Datensatzes ein definiertes Zeichen setze? Dann ließe sich durch Vergleichen des letzten Zeichens erkennen, ob die angegebene Länge korrekt war oder nicht (und dann z.B. die Verbindung trennen).



> Ansonsten is das ganze ziemlich stark vom konkreten Fall abhaengig, es ist also schwierig hier allgemeingueltige Tipps zu geben


Eine größere Datei soll ein kleinere Teile aufgeteilt werden, die dann nach und nach übertragen werden. Daher soll neben dem eigentlichen Daten-Teil in dem selben Datensatz noch die Gesamtanzahl der Teile und die Nummer des aktuellen Teils übertragen werden.


----------



## schalentier (28. Okt 2010)

Ich bin mir nicht sicher, glaube aber, ich missverstehe irgendwas.

Du benutzt doch TCP, noch dazu die Streaming API von Java. Die wurde entwickelt, eben genau dafuer, dass ein Benutzer dieser API sich nicht mehr um Low-Level-Kram, wie Fehlerkorrektur oder Packetgroessen kuemmern muss.

Wenn du etwas in den Stream schreibst, kommt das genau so auf der andren Seite wieder an. Intern wird der Stream natuerlich in einzelne Packete zerlegt, mit einer fortlaufenden Nummer, Pruefsumme und anderen Sachen versehen und auf Empfaengerseite "fehlerkorrigiert", sortiert und wieder zusammengesetzt. Das macht imho TCP.

Kurz: Ich sehe ueberhaupt keinen Vorteil im Zerlegen der Datei in einzelne Portionen, da das eh (vermutlich von deinem Netzwerktreiber) gemacht wird. Das was du machen willst, ergibt eigentlich nur dann Sinn, wenn du UDP verwenden moechtest. Dann musst du dich um alles selbst kuemmern, was in einigen Faellen durchaus Vorteile mit sich bringt, ist aber eine Wissenschaft fuer sich ;-)


----------



## mainfield (28. Okt 2010)

Ich habe einen Rechner, der per WLan ans Netzwerk angebunden ist. Da die Signalqualität relativ schlecht ist, bricht die Verbindung leider ab und zu zusammen.
Wenn ich auf oder von diesem Rechner eine größere Datei übertragen möchte (mehrere 100 MB), dann fange ich oft mehrmals wieder von vorne an, weil die Verbindung abbricht. 
Daher möchte ich die Datei in kleinen Teilen übertragen, so dass ich nach einem Verbindungsabbruch nicht mehr von von vorn beginnen muss, sondern nur die noch fehlenden Teile übertragen muss (z.B. 100 Teile, nach Teil 49 bricht die Verbindung ab. Ich verbinde mich neu und setze die Übertragung mit Teil 50 fort).

Was ich mir noch überlegt habe: Ich könnte alle Bytes XOR-verknüpfen und das Ergebnis an das Ende des Datensatzes anhängen. Dann müsste sich relativ sicher erkennen lassen, ob die Auftrennung der Datensätze OK ist.


----------



## mabuhay (28. Okt 2010)

Hallo

Falls es dir nicht soo wichtig ist obs optimal ist, versende doch Objekte (ObjectOutput/InputStream). Gibt natürlich einen gewissen mehraufwand der zu sendenden Bytes, aber kenne deine Anwendung ja nicht ob das kritisch ist. Dann kannst du im Objekt noch mitsenden was auch immer du willst, anstatt bytes aneinanderzuhängen mit checksumme etc und wieder aufzuteilen.
Somit einfach für jedes Paket ein Objekt mit den bytes und einer nummer, und die bytes dann wieder beim Empfänger zusammensetzen...

mfg


----------

