Datei per UDP korrekt übertragen

jupp2

Mitglied
Hallo zusammen,

ich habe einen kleinen UDP-Server geschrieben, der eine Datei an seinen Client schicken soll. Der Effizienz halber möchte ich nicht ein byte-Array erstellen, das so groß wie die Datei ist, sondern ich versuche, die Datei in viele kleine Pakete zu unterteilen und diese dann zu verschicken. Auf der Serverseite sieht das dann so aus:

Java:
byte[] data = new byte[1024];			
in = new BufferedInputStream(new FileInputStream(path));
while (in.read(data) != -1) {
DatagramPacket packet = new DatagramPacket(data, dataLength, address, port);
udpSocket.send(packetToSend);

Die Übertragung klappt und es kommen alle Pakete an. Es gibt jedoch ein Problem. Die Dateien sind eigentlich immer korrupt oder stimmen nicht zu 100 % mit der Originaldatei überein. Ich glaube, den Grund zu kennen, doch weiß ich mir gerade nicht zu helfen.

Da die Pakete eine feste Größe von 1024 Byte haben, werden hinten immer ein paar undefinierte Bytes drangehängt. Daher ist die übertragende Datei auch immer leicht größer.

Hat jemand eine Idee, wie ich das verhindern kann?
Danke
 
Zuletzt bearbeitet:
Hallo,

UDP ist für diesen Zweck nicht geeignet. Es können immer Pakete verloren gehen oder in beliebiger Reihenfolge ankommen, ohne dass du etwas davon merkst. Sobald du anfängst, diese Probleme durch diverse Maßnahmen zu lösen, bist du dabei, das zu implementieren, was TCP ausmacht.

Warum möchtest du denn UDP verwenden, wenn du eigentlich TCP brauchst?
 
Ich muss UDP verwenden, weil es die Aufgabenstellung so will 🙂

Also kann man hier nur tricksen.

Es funktioniert, wenn ich erst dem Client sage, wie viele Bytes übertragen werden. Doch muss ich davon ausgehen, dass diese Information ganz am Schluss kommt, weil Du hast ja recht: ich kenne die Reihenfolge nicht.

Oder gibts vielleicht einen eleganten Trick?
 
Du könntest jedes Paket prinzipiell mit einem int am Anfang versehen, das für die Sequenznummer steht. Und bei Paket 0 vieleicht zufällig, wie viele Pakete oder Bytes kommen werden.

Dann musst du dich aber noch darum kümmern, dem Client mitzuteilen, dass etwas nochmal gesendet werden muss.. aber da weißt du wiederum nicht, ob der Client diese Info auch tatsächlich bekommt.. das ist kein spaßiges Thema :/
 
Das ist eine gute Idee. Danke. Dafür muss ich die Seriennummer zunächst in eine Binärzahl umwandeln und die einzelnen Bits dann in dem bytes Array speichern, das ich versenden werde. Dabei habe ich jedoch noch ein anderes Problem:

Java:
private static byte[] convert(int i ) {
			ByteBuffer b = ByteBuffer.allocate(5);
			b.putInt(i);

			byte[] result = b.array();
			return result;
		}

Solch eine Funktion liefert mir nicht das richtige Ergebnis. convert(10) liefert das Array "0,0,0,10,0"

Wie kann das sein? Ich hatte sowas erwartet: "0,1,0,1,0".

Danke
 
Ein int besteht aus 4 Bytes, deshalb liefert dir deine Convert-Methode auch 4 Bytes. Warum möchtest du irgendwas umständlich auf Bit-Ebene machen? Das ist gar nicht nötig 🙂 Wenn du b.putInt benutzt, kann die andere Seite b.getInt verwenden und alles wird gut.
 
Hallo 🙂

Ich muss aber doch sicherstellen, dass für die Seriennummer nur eine bestimmte Anzahl von Bits verwendet wird. Ich möchte der Seriennummer 32 Bits spendieren. Dahinter sollen dann die Daten kommen.

Ich dachte, dass ich auf Clientseite dann einfach die ersten 32 Bits auslese und diese als Seriennummer interpretiere.

Nur mit b.putInt weiß ich doch nicht, wie viele Bits für die Seriennummer reserviert worden sind.

Viele Grüße
 
Doch, weil ein int immer aus 32 Bits besteht. Der Triumph der Mathematik über die Politik ist zwar, dass führende Nullen gestrichen werden - aber in der Informatik hat man sich trotzdem darauf geeignet, feste Größen zu verwenden. Wenn du mit putInt eine 0 in das Paket schreibst, werden da 32 0-Bits eingefügt. Du hast also kein Problem, solange deine Länge noch in 32 Bit darstellbar ist - da Java leider nur signed ints beherrscht, sind das ca. 2GB.
 
ah. Das ist ja cool. Aber das bedeutet, dass eine Größe von 16 Bits für die Seriennummer nich so einfach realisiert werden können? Oder eine beliebige Anzahl von Bits..wie 19 🙂 Ich muss also die Bitlänge eines primitiven Datentyps wie int nehmen?

Also kann ich einfach ein byte Array für die Daten, ein byte Array für die Seriennummer nehmen. Diese konkatiniere ich und verschick sie. Ist das eine gute Idee oder denke ich wieder zu kompliziert?


Danke für die tolle Hilfe
 
Zuletzt bearbeitet:
Genau, die primitiven Datentypen kannst du sehr einfach übertragen:

* byte mit 8 Bit
* short mit 16 Bit
* int mit 32 Bit
* long mit 64 Bit

Wenn man 19 Bit übertragen möchte, würde man üblicherweise einen int nehmen - ansonsten müsste man ja alle folgenden Bytes auf Bit-Ebene so verschieben, dass dann alles total konfus ist. Sowas lässt man lieber sein 😉

Du denkst trotzdem noch zu kompliziert 😉 Wenn deine Daten als byte-Array vorliegen, machst du das einfach so:

* erstell einen ByteBuffer der Größe des Arrays + 4 (für den int)
* buffer.putInt(nummer)
* buffer.put(array, 0, array.length)

Danach kannst du den Buffer entweder so verschicken, falls die verschicken-Methode das kann, oder du wandelst eben den Buffer wieder in ein byte[]-Array um. Dann wäre allerdings ein ByteArrayOutputStream besser geeignet, weil du dann nicht zwei Arrays brauchst.
 
Hey, das mit dem ByteBuffer gefällt mir und ich möchte es ausprobieren.

Jetzt habe ich dabei noch ein kleines Problem. Ich verschicke mein Paket so:

Java:
while (in.read(data) != -1) {
  ByteBuffer b = ByteBuffer.allocate(len);
  b.putInt(serialNumber);
  b.put(data, 0, data.length);
  DatagramPacket pac = new DatagramPacket(b.array(), len, address, port);

  udpSocket.send(pac);
  serialNumber++;
}

Der Client erhält es so:

Java:
clientSocket.receive(packetReceived);
b.wrap(packetReceived.getData());
System.out.println(b.getInt());
//...

Als Test möchte ich also die Seriennummer auf der Clientseite ausgeben. Der gibt jedoch konstant 0 aus. Wie kann das sein? Mache ich noch einen Fehler bei der Verwendung von getInt und putInt?



Grüße
 
Zuletzt bearbeitet:
Ich bin selbst auf den Fehler gekommen. Es muss so heißen:

Java:
b = Bytebuffer.wrap(packetReceived.getData());

...warum auch immer....
 

Zurück
Oben