| Characteristic | UDP | TCP |
| Connection setup | None (connectionless) | 3-way handshake |
| Reliability | Best-effort (no retransmit) | Guaranteed delivery |
| Ordering | Not guaranteed | In-order delivery |
| Overhead | 8-byte header | 20–60 byte header |
| Broadcast / Multicast | ✅ Supported | ❌ Not supported |
| Use cases | DNS, DHCP, VoIP, video, games | HTTP, FTP, SMTP, databases |
A UDP "server" receives datagrams on a fixed port; a UDP "client" sends datagrams and optionally waits for a reply.
// UDP Server — receives a datagram and echoes it back
import java.net.*;
public class UdpEchoServer {
public static void main(String[] args) throws Exception {
try (DatagramSocket socket = new DatagramSocket(9999)) {
System.out.println("UDP server listening on port 9999");
byte[] buf = new byte[1024];
while (true) {
DatagramPacket request = new DatagramPacket(buf, buf.length);
socket.receive(request); // blocks until datagram arrives
String msg = new String(request.getData(), 0, request.getLength());
System.out.println("Received: " + msg);
// echo back to sender
byte[] response = ("ECHO: " + msg).getBytes();
DatagramPacket reply = new DatagramPacket(
response, response.length,
request.getAddress(), request.getPort());
socket.send(reply);
}
}
}
}
// UDP Client — sends a message and waits for the echo
import java.net.*;
public class UdpEchoClient {
public static void main(String[] args) throws Exception {
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(3000); // 3 s receive timeout
InetAddress server = InetAddress.getByName("localhost");
byte[] data = "Hello UDP!".getBytes();
// send
DatagramPacket packet = new DatagramPacket(data, data.length, server, 9999);
socket.send(packet);
// receive echo
byte[] buf = new byte[1024];
DatagramPacket reply = new DatagramPacket(buf, buf.length);
socket.receive(reply);
System.out.println(new String(reply.getData(), 0, reply.getLength()));
}
}
}
Calling socket.connect(address, port) on a DatagramSocket restricts it to a single peer — datagrams from other addresses are silently discarded. It also lets the OS detect ICMP "port unreachable" errors as PortUnreachableException.
DatagramSocket socket = new DatagramSocket();
socket.connect(InetAddress.getByName("192.168.1.100"), 9999);
// send without specifying address — uses connected peer
byte[] data = "ping".getBytes();
socket.send(new DatagramPacket(data, data.length));
// receive — only accepts packets from connected peer
byte[] buf = new byte[256];
DatagramPacket pkt = new DatagramPacket(buf, buf.length);
socket.receive(pkt);
socket.disconnect(); // back to unconnected mode
Broadcast sends a datagram to all hosts on the local subnet. The destination address is the subnet broadcast address (e.g. 255.255.255.255). setBroadcast(true) must be called on the sender.
// Broadcast sender
try (DatagramSocket socket = new DatagramSocket()) {
socket.setBroadcast(true);
byte[] data = "DISCOVER".getBytes();
InetAddress broadcast = InetAddress.getByName("255.255.255.255");
socket.send(new DatagramPacket(data, data.length, broadcast, 8888));
System.out.println("Broadcast sent");
}
// Broadcast receiver (binds to the broadcast port)
try (DatagramSocket socket = new DatagramSocket(8888)) {
socket.setBroadcast(true);
byte[] buf = new byte[256];
DatagramPacket pkt = new DatagramPacket(buf, buf.length);
socket.receive(pkt);
System.out.println("Discovered: " + pkt.getAddress());
}
Broadcast is limited to the local subnet and is blocked by most routers. Use multicast for cross-subnet group delivery.
Multicast delivers datagrams to a group of subscribed hosts identified by a Class D IP address (224.0.0.0 – 239.255.255.255). Use MulticastSocket — a subclass of DatagramSocket — to join and leave groups.
// Multicast receiver — joins a group on a specific network interface
import java.net.*;
public class MulticastReceiver {
private static final String GROUP = "239.1.2.3";
private static final int PORT = 7777;
public static void main(String[] args) throws Exception {
InetAddress group = InetAddress.getByName(GROUP);
NetworkInterface ni = NetworkInterface.getByName("eth0"); // use your interface
try (MulticastSocket socket = new MulticastSocket(PORT)) {
socket.joinGroup(new InetSocketAddress(group, PORT), ni);
System.out.println("Joined multicast group " + GROUP);
byte[] buf = new byte[1024];
for (int i = 0; i < 5; i++) {
DatagramPacket pkt = new DatagramPacket(buf, buf.length);
socket.receive(pkt);
System.out.println("Got: " + new String(pkt.getData(), 0, pkt.getLength()));
}
socket.leaveGroup(new InetSocketAddress(group, PORT), ni);
}
}
}
// Multicast sender
public class MulticastSender {
public static void main(String[] args) throws Exception {
try (DatagramSocket socket = new DatagramSocket()) {
InetAddress group = InetAddress.getByName("239.1.2.3");
byte[] data = "Hello Multicast!".getBytes();
for (int i = 0; i < 5; i++) {
socket.send(new DatagramPacket(data, data.length, group, 7777));
Thread.sleep(500);
}
}
}
}
Multicast TTL (Time To Live) limits how many router hops a datagram traverses. Set it with socket.setTimeToLive(n). TTL=1 stays on the local subnet; TTL=32 crosses regional networks.
UDP datagrams are limited to 65,507 bytes (IPv4: 65,535 − 20 IP header − 8 UDP header). In practice keep payloads under 1,472 bytes to avoid IP fragmentation on a typical Ethernet MTU of 1,500 bytes.
| Concern | Solution |
| Packet loss | Application-level ACK + retransmit (like QUIC / custom ARQ) |
| Reordering | Sequence numbers in the payload; buffer and reorder on receiver |
| Duplication | Track seen sequence numbers; discard duplicates |
| Large messages | Fragmentation at application layer with reassembly buffer |
// Simple sequenced packet — prepend a 4-byte sequence number
ByteBuffer buf = ByteBuffer.allocate(4 + payload.length);
buf.putInt(seqNum++);
buf.put(payload);
byte[] data = buf.array();
socket.send(new DatagramPacket(data, data.length, address, port));
// Receiver: extract sequence number
DatagramPacket pkt = new DatagramPacket(new byte[1024], 1024);
socket.receive(pkt);
ByteBuffer received = ByteBuffer.wrap(pkt.getData(), 0, pkt.getLength());
int seq = received.getInt();
byte[] body = new byte[received.remaining()];
received.get(body);
| Pitfall | Fix |
| Buffer too small → truncated data | Always allocate a buffer at least as large as the max expected datagram |
| Assuming delivery → silent data loss | Design for best-effort; add ACK if reliability matters |
| Multicast on wrong interface | Specify NetworkInterface explicitly in joinGroup |
| Firewall blocks multicast / broadcast | Open UDP ports; test on loopback first |
Not calling setBroadcast(true) | Broadcast sends silently fail or throw on some JVMs |
| Reusing DatagramPacket without resetting length | Reset pkt.setLength(buf.length) before each receive call |