W3docs

Java Datagram Sockets (UDP)

UDP-Datagramme in Java senden und empfangen mit DatagramSocket und DatagramPacket.

Socket und ServerSocket verwenden TCP — einen zuverlässigen, geordneten, verbindungsbasierten Stream. DatagramSocket verwendet UDP, den anderen Transport: verbindungslos und paketbasiert. Es gibt kein „Verbinden"; man sendet unabhängige Datagramme an eine Adresse und hofft, dass sie ankommen. Es gibt keinen Handshake, keine Reihenfolgengarantie und keine Zustellungsgarantie — dafür aber keinen Verbindungsaufwand und sehr geringe Latenz.

Diese Seite erklärt, wann UDP die richtige Wahl ist, die beiden Kernklassen (DatagramSocket und DatagramPacket), ein vollständiges Anfrage/Antwort-Beispiel zum Ausführen sowie die häufigsten Fallstricke für UDP-Einsteiger. Wer noch keine Erfahrung mit Netzwerken in Java hat, sollte mit der Netzwerkeinführung beginnen.

Wann UDP das richtige Mittel ist

UDP tauscht Zuverlässigkeit gegen Geschwindigkeit und Einfachheit. Es eignet sich, wenn:

  • Gelegentliche Verluste akzeptabel sind — Live-Audio/Video, Spielzustand, Telemetrie. Ein verlorenes Frame ist besser als ein verzögertes.
  • Nachrichten klein und eigenständig sind — DNS-Anfragen, NTP-Zeitsynchronisation.
  • Man an viele Empfänger broadcast/multicast — TCP kann das nicht.

Wird jedes Byte in der richtigen Reihenfolge benötigt (Dateiübertragung, Webseiten, Datenbanken), sollte TCP verwendet werden. Viele Anwendungen legen eine eigene leichtgewichtige Zuverlässigkeitsschicht über UDP, statt den vollen Aufwand von TCP zu zahlen.

Die zwei Klassen

UDP in Java verwendet ein Paar:

  • DatagramSocket — der Endpunkt, von dem aus man send und auf dem man receive aufruft. Es gibt keinen separaten „Server-Socket"; dieselbe Klasse übernimmt beide Rollen, da es keine Verbindung zum Akzeptieren gibt.
  • DatagramPacket — ein Datagramm: ein Byte-Puffer sowie beim Senden die Zieladresse und der Port; beim Empfangen werden Adresse und Port des Absenders sowie die Datenlänge eingetragen.
DatagramSocket socket = new DatagramSocket(9000);     // bind to receive on 9000
byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
DatagramPacket out = new DatagramPacket(data, data.length, address, port);
socket.send(out);                                     // fire and forget

byte[] buf = new byte[1024];
DatagramPacket in = new DatagramPacket(buf, buf.length);
socket.receive(in);                                   // blocks; fills buf + sender info

Ein wichtiges Detail: Nach receive() sollte man genau in.getLength() Bytes aus dem Puffer lesen — der Puffer hat eine feste Größe, das Datagramm kann aber kürzer sein. Mit setSoTimeout(ms) verhindert man, dass ein verlorenes Paket receive() dauerhaft blockiert.

Ein Beispiel: UDP-Anfrage/Antwort über Loopback

Dieses Programm startet einen Empfänger in einem Hintergrund-Thread, der auf ein Datagramm wartet und dem Absender antwortet, während der Haupt-Thread ein Datagramm sendet und die Bestätigung liest — ein vollständiger UDP-Roundtrip über das Loopback-Interface.

java— editable, runs on the server

Was man aus dem Ablauf mitnehmen kann:

  • Es gab kein connect() und kein accept(). Beide Seiten sind einfach DatagramSockets; der Sender hat ein Paket an eine Adresse und einen Port gesendet, der Empfänger hat es aufgenommen. UDP hat keine Verbindung, daher übernimmt dieselbe Klasse beide Rollen — die Asymmetrie der TCP-Client/Server-Sockets entfällt.
  • Ein DatagramPacket enthielt sowohl Daten als auch Adressinformationen. Der Empfänger erfuhr wer die Anfrage gesendet hat über request.getAddress() und request.getPort() und antwortete direkt an diesen Endpunkt — es gibt keinen dauerhaften Kanal, daher muss jede Antwort explizit adressiert werden.
  • Der Inhalt wurde mit new String(data, 0, getLength(), …) dekodiert, nicht der gesamte 1024-Byte-Puffer. Ein Datagramm füllt nur einen Teil des festen Puffers; das Lesen von getLength() Bytes ist Pflicht, sonst hängt man nachgelagerte Garbage-Bytes aus dem ungenutzten Pufferbereich an.
  • setSoTimeout(2000) schützte das receive(). Da UDP nichts garantiert, würde ein verlorenes Paket andernfalls dauerhaft blockieren; ein Timeout verwandelt „Paket nie angekommen" in eine abfangbare SocketTimeoutException, die man wiederholen oder melden kann.
  • Der Austausch funktionierte hier, weil Loopback verlustfrei und geordnet ist, die API hat jedoch kein solches Versprechen gegeben. Über ein echtes Netzwerk könnte dieses Datagramm verschwinden, zweimal ankommen oder nach einem späteren eintreffen — genau deshalb wählen zuverlässigkeitssensitive Anwendungen TCP oder bauen ihr eigenes Bestätigungsschema auf UDP auf.

Häufige Fallstricke

Einige Fallen erwischen fast jeden beim ersten Umgang mit DatagramSocket:

  • Den gesamten Puffer statt getLength() Bytes lesen. Der Puffer hat eine feste Größe; das Datagramm meistens nicht. Immer mit new String(data, 0, packet.getLength(), …) oder Arrays.copyOf(data, packet.getLength()) slicen. Bei Wiederverwendung des Puffers wird das schlimmer — übrig gebliebene Bytes eines vorherigen, längeren Datagramms erscheinen als nachgelagerte Garbage.
  • Kein Timeout für receive(). Da UDP niemals Zustellung verspricht, blockiert ein verlorenes Paket receive() dauerhaft. setSoTimeout(ms) aufrufen und die resultierende SocketTimeoutException behandeln (wiederholen, protokollieren oder abbrechen).
  • Mehr senden als in ein Datagramm passt. UDP hat kein Streaming; ein send() ist ein Paket. Große Nutzdaten werden durch IP fragmentiert, und ein einziges verlorenes Fragment verwirft das gesamte Datagramm. Nutzdaten klein halten — etwa 512 Bytes ist eine sichere Obergrenze, die Fragmentierung in den meisten Netzwerken vermeidet.
  • Das Schließen des Sockets vergessen. Ein DatagramSocket hält einen OS-Port. Try-with-resources verwenden (es implementiert AutoCloseable) oder in einem finally-Block schließen, damit der Port freigegeben wird.
  • Annehmen, die Antwort kommt von dort, wo man hingesendet hat. receive() überschreibt die Adresse und den Port des Pakets mit dem tatsächlichen Absender. Immer mit packet.getAddress()/packet.getPort() antworten statt mit einem hartkodierten Ziel.

Für garantierte, geordnete Zustellung greift man auf die TCP-Klassen in Java Sockets zurück.

Übungen

Übung
Ein Monitoring-Agent empfängt UDP-Datagramme in einen wiederverwendeten 'byte[2048]'-Puffer via 'socket.receive(packet)' und konvertiert dann den gesamten Puffer mit 'new String(packet.getData(), StandardCharsets.UTF_8)'. Kurze Nachrichten enthalten nachgelagerte Garbage. Was ist der Fix?
Ein Monitoring-Agent empfängt UDP-Datagramme in einen wiederverwendeten 'byte[2048]'-Puffer via 'socket.receive(packet)' und konvertiert dann den gesamten Puffer mit 'new String(packet.getData(), StandardCharsets.UTF_8)'. Kurze Nachrichten enthalten nachgelagerte Garbage. Was ist der Fix?
Was this page helpful?