UDP Sockets in Java: DatagramSocket, DatagramPacket & Multicast

UDP (User Datagram Protocol) is connectionless and unreliable (packets may be lost, reordered, or duplicated) but it is fast and lightweight. It is the foundation of DNS, DHCP, video streaming, and gaming protocols. Java exposes UDP through DatagramSocket and DatagramPacket.

Share

UDP vs TCP: When to Use Each

CharacteristicUDPTCP
Connection setupNone (connectionless)3-way handshake
ReliabilityBest-effort (no retransmit)Guaranteed delivery
OrderingNot guaranteedIn-order delivery
Overhead8-byte header20–60 byte header
Broadcast / Multicast✅ Supported❌ Not supported
Use casesDNS, DHCP, VoIP, video, gamesHTTP, FTP, SMTP, databases

Unicast UDP Server & Client

A UDP "server" receives datagrams on a fixed port; a UDP "client" sends datagrams and optionally waits for a reply.

Java
// 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);
            }
        }
    }
}
Java
// 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()));
        }
    }
}

Connected UDP Socket

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.

Java
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

UDP Broadcast

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.

Java
// 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.

IP Multicast with MulticastSocket

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.

Java
// 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);
        }
    }
}
Java
// 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.

Packet Size Limits & Application-Level Reliability

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.

ConcernSolution
Packet lossApplication-level ACK + retransmit (like QUIC / custom ARQ)
ReorderingSequence numbers in the payload; buffer and reorder on receiver
DuplicationTrack seen sequence numbers; discard duplicates
Large messagesFragmentation at application layer with reassembly buffer
Java
// 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);

Common Pitfalls

PitfallFix
Buffer too small → truncated dataAlways allocate a buffer at least as large as the max expected datagram
Assuming delivery → silent data lossDesign for best-effort; add ACK if reliability matters
Multicast on wrong interfaceSpecify NetworkInterface explicitly in joinGroup
Firewall blocks multicast / broadcastOpen UDP ports; test on loopback first
Not calling setBroadcast(true)Broadcast sends silently fail or throw on some JVMs
Reusing DatagramPacket without resetting lengthReset pkt.setLength(buf.length) before each receive call