Networking

Using datagrams in Java

Sometimes, the overhead of TCP is too much for your Internet application to bear. If speed or raw performance matter more than the reliable connection-oriented support of TCP, you can turn to the lower-level User Datagram Protocol (UDP) instead.


The User Datagram Protocol (UDP) allows an application to send data in packets with a low overhead and is employed in some important standards, like DNS and RADIUS. Many programmers may not be familiar with the protocol, though, since it is harder to program and less frequently used than TCP. But it's worth learning how to use because UDP is appropriate whenever you need to exchange data between applications as messages, without the overhead of creating a TCP connection. It is the ideal protocol when the messages are short and you don’t need the concept of a connection.

UDP basics
The User Datagram Protocol (UDP) sends and receives data as packets (datagrams). Unlike TCP, there is no concept of a connection, each datagram is routed and sent individually, and there is no guarantee of delivery. And when several datagrams are sent to the same address, they can even be delivered in a different order. If you need services like guaranteed delivery and guaranteed ordering, they must be implemented by the application as a higher level protocol on top of UDP in the form of confirmation packets, timeouts and retransmission, packet numbering, etc. Or you should consider using TCP instead.

Like TCP, in order to send and receive datagrams you need an appropriate socket, in this case an instance of the java.net.DatagramSocket class. Unlike TCP, however, there is no distinction between client and server sockets, that is, a DatagramSocket can both send and receive datagrams at any time.

When a DatagramSocket is instantiated, it is also bound (associated) to an address and port on the local machine by its constructors. The binding will determine the source address and port of packets sent through the socket and the address and port the sockets listen to when receiving packets.

If the local machine has several IP addresses, you can bind the socket to a specific address or to the wildcard address, a special address that means all IP addresses of the local machine. In this case, the socket will listen to all addresses for incoming packets, but the source address of outgoing packets will depend on the interface that is used to send it, as determined by the local machine routing table.

Likewise, you can bind the socket to a specific port (useful if you are going to use the socket as a server, so that clients know to which port to send packets) or you can let Java pick up any available port.

Sending and receiving data
Packets to be sent or received are represented by instances of the java.net.DatagramPacket class. To send a packet you must instantiate a DatagramPacket, set its port and IP address to the port and IP address of the host to where you want to send the packet and set its data buffer with the data you want to send.

The data buffer is specified as an array of bytes, but you can use just part of an array specifying the offset and the length of the piece you want to use. The class DatagramPacket provides several constructors and methods to set the address, port, and data buffer.

Once you have constructed and filled a DatagramPacket, you can send it using the method DatagramSocket.send (DatagramPacket). Notice that since UDP is connectionless, the method success does not mean the packet has been delivered to the destination, only that it has been sent by the TCP/IP stack. Delivery confirmation, if needed, must be implemented at a higher level within your application.

In order to receive a packet, you must construct a DatagramPacket, set a data buffer large enough to hold the data you expect to receive, and call the method DatagramSocket.receive(DatagramPacket).

Upon return, the data buffer of the DatagramPacket passed as parameter will hold the data received. If the data is larger than the buffer size, excess bytes are silently discarded. The DatagramPacket will also contain the address and port of the sender, which can be queried by appropriate methods.

Notice that DatagramSocket.receive will block forever until a packet is received, unless you have previously set a timeout on the DatagramSocket using the method DatagramSocket.setSoTimeout(int), which sets a maximum time, in milliseconds, to wait for a packet. If a packet is not received within the specified timeout, a java.net.SocketTimeoutException will be thrown.

Connecting to a remote peer
One disadvantage of UDP is that you must specify the destination host’s address and port on every packet to be sent. Another is that UDP sockets accept packets from any host. That is a consequence of the connectionless nature of UDP.

However, the DatagramSocket class provides the concept of a connection to a remote host that greatly simplifies implementations based on UDP. That feature is implemented by the method DatagramSocket.connect(), which connects the socket to a remote host.

While the socket is connected, the send method will only send packets to the connected host. That means you can leave the address and port of DatagramPackets you are going to send unset. In fact, if you set them to anything other than the connected host’s address and port, you will get an exception. Likewise, the receive method will accept packets only from the connected host’s address and port.

Notice that connect just filters the packets sent and received; that is, it does not establish a real connection with the remote host, so the method itself does not send anything and never fails, unlike TCP.

A simple example
Listing A is a simple server that implements the UDP standard echo protocol. The echo protocol is very simple, it just echoes back (sends to the sender) any datagram received on port 7.

The server is straightforward; it just creates a DatagramSocket, binds it to the port 7 and creates a DatagramPacket to receive the datagrams. Notice that I arbitrarily chose a buffer of 512 bytes to hold incoming packets (the echo protocol does not specify the maximum size for data), so any data larger than that will be discarded.

Then the server enters an infinite loop waiting for packets. Any packet received is just sent back to the sender (notice that the DatagramPacket is filled with the sender information upon receive). The server also writes to the standard output the time when the packet was received, the sender address, and the packet data. Although the echo protocol does not mandate that the data must be text, I assume it is and convert the data to text using the platform’s default character set.

Listing B is a simple client for the echo protocol. It accepts the server address and the text to send as command line parameters, sends the text to the server, and prints to the standard output the answer delay and the text returned. Notice that I set a timeout of 5 seconds on the socket, so that if the server does not respond, I get an exception.

Although UDP is trickier than TCP to use, it is at the heart of important protocols. Since UDP is simpler and has less features than TCP, it requires less memory and imposes less overhead, making it the ideal candidate for resource-constrained devices. This is why I think it is important to know UDP and how to use it in Java.

Editor's Picks