W3docs

Java Datagram Sockets (UDP)

Send and receive UDP datagrams in Java with DatagramSocket and DatagramPacket.

Socket and ServerSocket speak TCP — a reliable, ordered, connection-based stream. DatagramSocket speaks UDP, the other transport: connectionless and packet-based. You do not "connect"; you fire independent datagrams at an address and hope they arrive. There is no handshake, no ordering, and no delivery guarantee — and in exchange, no connection overhead and very low latency.

This page covers when UDP is the right choice, the two core classes (DatagramSocket and DatagramPacket), a complete request/reply example you can run, and the gotchas that bite first-time UDP programmers. If you are new to networking in Java, start with the networking introduction.

When UDP is the right tool

UDP trades reliability for speed and simplicity. It fits when:

  • Occasional loss is acceptable — live audio/video, game state, telemetry. A dropped frame is better than a delayed one.
  • Messages are small and self-contained — DNS queries, NTP time sync.
  • You broadcast/multicast to many receivers — TCP cannot.

If you need every byte, in order (file transfer, web pages, databases), use TCP. Many applications layer their own light reliability on top of UDP rather than pay TCP's full cost.

The two classes

UDP in Java uses a pair:

  • DatagramSocket — the endpoint you send from and receive on. There is no separate "server socket"; the same class does both, because there is no connection to accept.
  • DatagramPacket — one datagram: a byte buffer plus, for sending, the destination address and port; for receiving, it is filled in with the sender's address and port and the data length.
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

A critical detail: after receive(), read exactly in.getLength() bytes from the buffer — the buffer is fixed-size but the datagram may be shorter. Set setSoTimeout(ms) so a lost packet does not block receive() forever.

A worked example: a UDP request/reply over loopback

This program runs a receiver on a background thread that waits for one datagram and replies to its sender, while the main thread sends a datagram and reads the acknowledgement — a full UDP round trip on the loopback interface.

java— editable, runs on the server

What to take from the run:

  • There was no connect() and no accept(). Both ends are just DatagramSockets; the sender fired a packet at an address and port, and the receiver picked it up. UDP has no connection, so the same class handles both roles — the asymmetry of TCP's client/server sockets disappears.
  • A DatagramPacket carried both data and addressing. The receiver learned who sent the request from request.getAddress() and request.getPort() and replied straight to that endpoint — there is no persistent channel, so every reply must be addressed explicitly.
  • The body was decoded with new String(data, 0, getLength(), …), not the whole 1024-byte buffer. A datagram fills only part of a fixed buffer; reading getLength() bytes is mandatory, or you append trailing garbage from the unused buffer space.
  • setSoTimeout(2000) guarded the receive(). Because UDP guarantees nothing, a lost reply would otherwise block forever; a timeout turns "packet never came" into a catchable SocketTimeoutException you can retry or report.
  • The exchange worked here because loopback is lossless and ordered, but the API made no such promise. Over a real network this datagram could vanish, arrive twice, or arrive after a later one — which is exactly why reliability-sensitive applications choose TCP or build their own acknowledgement scheme on top of UDP.

Common gotchas

A few traps catch nearly everyone the first time they use DatagramSocket:

  • Reading the whole buffer instead of getLength() bytes. The buffer is fixed-size; the datagram usually is not. Always slice with new String(data, 0, packet.getLength(), …) or Arrays.copyOf(data, packet.getLength()). Reusing a buffer makes this worse — leftover bytes from a previous, longer datagram appear as trailing garbage.
  • No timeout on receive(). Because UDP never promises delivery, a lost packet leaves receive() blocked forever. Call setSoTimeout(ms) and handle the resulting SocketTimeoutException (retry, log, or give up).
  • Sending more than fits in one datagram. UDP has no streaming; one send() is one packet. Large payloads get fragmented by IP and a single lost fragment drops the whole datagram. Keep payloads small — roughly 512 bytes is a safe ceiling that avoids fragmentation on most networks.
  • Forgetting to close the socket. A DatagramSocket holds an OS port. Use try-with-resources (it implements AutoCloseable) or close it in a finally block so the port is released.
  • Assuming the reply comes from where you sent it. receive() overwrites the packet's address and port with the actual sender. Always reply using packet.getAddress()/packet.getPort() rather than a hard-coded destination.

For guaranteed, ordered delivery instead, reach for the TCP classes in Java sockets.

Practice

Practice
A monitoring agent receives UDP datagrams into a reused 'byte[2048]' buffer via 'socket.receive(packet)', then converts the whole buffer with 'new String(packet.getData(), StandardCharsets.UTF_8)'. Short messages come out with trailing garbage. What is the fix?
A monitoring agent receives UDP datagrams into a reused 'byte[2048]' buffer via 'socket.receive(packet)', then converts the whole buffer with 'new String(packet.getData(), StandardCharsets.UTF_8)'. Short messages come out with trailing garbage. What is the fix?
Was this page helpful?