UDP and Multicasting in Java

User Datagram Protocol (UDP) sits on top of IP and provides an unreliable counterpart to TCP. UDP sends individual packets between two nodes in a network. UDP packets do not have knowledge of other packets, and there is no guarantee that a packet will actually arrive at its intended destination. When multiple packets are sent, there is no guarantee of the arrival order. UDP messages are simply sent and then forgotten as there are no acknowledgements sent from a recipient. UDP is a connectionless protocol. There is no exchange of messages between two nodes to facilitate the packet transmission.

No state information is maintained about the connection. UDP is appropriate for services where delivery needs to be efficient, and no guarantee of delivery is needed. For example, it is used for Domain Name System (DNS) services, Network Time Protocol (NTP) services, Voice Over IP (VOIP), network communication coordination by P2P networks, and for video streaming. If a video frame is lost, then the viewer may never notice it if the loss does not occur frequently. There are several protocols that use UDP, including:

Real Time Streaming Protocol (RTSP): This protocol is used to control the streaming of media

Routing Information Protocol (RIP): This protocol determines the route that is used to transmit packets

Domain Name System (DNS): This protocol looks up an Internet domain name and returns its IP address

Network Time Protocol (NTP): This protocol synchronizes clocks across the Internet A UDP packet consists of an IP address and port number to identify its destination. The UDP packets have a fixed size and can be as large as 65,353 bytes. However, each packet uses a minimum of 20 bytes for an IP header and 8 bytes for a UDP header, limiting the size of a message to 65,507 bytes. If a message is larger than that, then multiple packets will need to be sent. UDP packets can also be multicast. This means that a packet is sent to every node that belongs to a UDP group. This is an efficient way of sending information to multiple nodes without having to explicitly target each node. Instead, the packet is

sent to a group whose members are responsible for capturing its group’s packets. In this techbinz, we will illustrate how the UDP protocol can be used to:

• Support the traditional client/server model

• Use NIO Channels to perform UDP operations

• Multicast packets to group members

• Stream media such as audio or video to a client

We will start with an overview of Java support for UDP and provide more UDP protocol details.

Java support for UDP

Java uses the DatagramSocket class to form socket connections between nodes. The DatagramPacket class represents a packet of data. Simple send and receive methods will transmit the packets across a network. UDP uses an IP address and a port number to identify nodes. UDP port numbers range from 0 to 65535. Port numbers are broken down into three types:

• Well-known ports (0 to 1023): These are port numbers that are used for relatively common services.

• Registered ports (1024 to 49151): These are port numbers that are assigned by IANA to a process.

• Dynamic/private ports (49152 to 65535): These are dynamically assigned to clients when a connection is initiated. These are normally temporary and cannot be assigned by IANA.

The following table is a short list of UDP specific port assignments. They illustrate how UDP is widely used to support many diverse applications and services. A more complete list of TCP/UDP port numbers is found at https://en.wikipedia.org/

wiki/List_of_TCP_and_UDP_port_numbers:

Well-known ports (0 to 1023)Usage
7This is the echo protocol
9This means wake-on-LAN
161This is the Simple Network Management Protocol (SNMP)
319These are Precision Time Protocol (PTP) event messages
320These are PTP general messages
513This indicates who the user is
514This is the syslog—used for system logging
520This is the Routing Information Protocol (RIP)
750This is kerberos-iv, Kerberos version IV
944This is the network file system service
973This is the network file system over IPv6 service

The following table gives a list of the registered ports and their usage:

Registered ports (1024 to 49151)Usage
1534This is used for Eclipse Target Communication Framework (TCF)
1581This is used for MIL STD 2045-47001 VMF
1589This is used for Cisco VLAN Query Protocol (VQP) / VMPS
2190This is used for TiVoConnect Beacon
2302This is used for Halo: Combat Evolved multiplayer
3000This is used for BitTorrent sync
4500This is used for IPSec NAT traversal
5353This is used for Multicast DNS (mDNS)
9110This is used for SSMP message protocol
27500 to 27900This is used for id Software’s QuakeWorld
29900 to 29901This is used for Nintendo Wi-Fi connection
36963This is used for Unreal Software multiplayer games

TCP versus UDP

There are several differences between TCP and UDP. These differences include the following:

Reliability: TCP is more reliable than UDP

Ordering: TCP guarantees the order of packet transmission will be preserved

Header size: The UDP header is smaller than the TCP header

Speed: UDP is faster than TCP

When a packet is sent using TCP, the packet is guaranteed to arrive. If it is lost, then it is re-sent. UDP does not offer this guarantee. If the packet does not arrive, then it is not re-sent. TCP preserves the order that packets are sent in, while UDP does not. If the TCP packets arrive at a destination in a different order than how they were sent, TCP will reassemble the packets in their original order. With UDP, this ordering is not preserved. When a packet is created, header information is attached to assist in the delivery of the packet. With UDP the header consists of 8 bytes. The usual size of a TCP header

is 32 bytes. With a smaller header size and lack of the overhead to ensure reliability, UDP is more efficient than TCP. In addition, less effort is required to create a connection. This efficiency makes it a better choice to stream media. Let’s begin our UDP examples with how a traditional client/server architecture is supported.

UDP client/server

The UDP client/server applications are similar in structure to the structure used for TCP client/server applications. On the server side, a UDP server socket is created, which waits for client requests. The client will create a corresponding UDP socket and use it to send a message to the server. The server can then process the request and send back a response.

A UDP client/server will use the DatagramSocket class for the socket and a DatagramPacket to hold the message. There is no restriction on the message’s content type. In our examples, we will be using a text message.

The UDP server application

Our server is defined next. The constructor will perform the work of the server:

public class UDPServer {

public UDPServer() {

System.out.println(“UDP Server Started”);

System.out.println(“UDP Server Terminating”);

}

public static void main(String[] args) {

new UDPServer();

}

}

In the constructor’s try-with-resources block, we create an instance of the DatagramSocket class. Several of the methods that we will be using may throw an IOException exception, which will be caught if necessary:

try (DatagramSocket serverSocket =

new DatagramSocket(9003)) {

}

} catch (IOException ex) {

//Handle exceptions

}

An alternate way of creating the socket is to use the bind method, as shown next. The DatagramSocket instance is created using null as the parameter. The port is then assigned with the bind method:

DatagramSocket serverSocket = new DatagramSocket(null);

serverSocket.bind(new InetSocketAddress(9003));

Both approaches will create a DatagramSocket instance using port 9003. The process of sending a message consists of the following:

• Creating an array of bytes

• Creating a DatagramPacket instance

• Using the DatagramSocket instance to wait for a message to arrive

The process is enclosed in a loop, as shown next, to allow multiple requests to be handled. The message that is received is simply echoed back to the client program. The DatagramPacket instance is created using the byte array and its length. It is used as the argument of the DatagramSocket class’s receive method. The packet does not hold any information at this time. This method will block until a request is made, and the packet will then be populated:

while (true) {

byte[] receiveMessage = new byte[1024];

DatagramPacket receivePacket = new DatagramPacket(

receiveMessage, receiveMessage.length);

serverSocket.receive(receivePacket);

}

When the method returns, the packet is converted into a string. If some other data type was sent, then some other conversion will be needed. The message that was sent is then displayed:

String message = new String(receivePacket.getData());

System.out.println(“Received from client: [” + message

+ “]\nFrom: ” + receivePacket.getAddress());

To send a response, the address and port number of the client are needed. These are obtained using the getAddress and getPort methods, respectively, against the packet, which possesses this information. We will see this when we discuss the client. Also needed is the message that is represented as an array of bytes, which the getBytes method provides:

InetAddress inetAddress = receivePacket.getAddress();

int port = receivePacket.getPort();

byte[] sendMessage;

sendMessage = message.getBytes();

A new DatagramPacket instance is created using the message, its length, and the client’s address and port number. The send method sends the packet to the client:

DatagramPacket sendPacket =

new DatagramPacket(sendMessage,

sendMessage.length, inetAddress, port);

serverSocket.send(sendPacket);

With the server defined, let’s examine the client.

The UDP client application

The client application will prompt the user for a message to send, and then it will send the message to the server. It will wait for a response and then display the response. It is declared here:

class UDPClient {

public UDPClient() {

System.out.println(“UDP Client Started”);

}

System.out.println(“UDP Client Terminating “);

}

public static void main(String args[]) {

new UDPClient();

}

}

The Scanner class supports getting user input. The try-with-resources block creates a DatagramSocket instance and handles exceptions:

Scanner scanner = new Scanner(System.in);

try (DatagramSocket clientSocket = new DatagramSocket()) {

}

clientSocket.close();

} catch (IOException ex) {

// Handle exceptions

}

The client’s current address is accessed using the getByName method, and a reference

to an array of bytes is declared. This address will be used to create a packet:

InetAddress inetAddress =

InetAddress.getByName(“localhost”);

byte[] sendMessage;

An infinite loop is used to prompt the user for messages. When the user enters “quit”,

the application will terminate, as shown here:

while (true) {

System.out.print(“Enter a message: “);

String message = scanner.nextLine();

if (“quit”.equalsIgnoreCase(message)) {

break;

}

}

UDP and Multicasting

[ 146 ]

To create a DatagramPacket instance holding the message, its constructor needs an array of bytes representing the message, its length, and the client’s address and port number. In the following code, the server’s port is 9003. The send method will send the packet to the server:

sendMessage = message.getBytes();

DatagramPacket sendPacket = new DatagramPacket(

sendMessage, sendMessage.length,

inetAddress, 9003);

clientSocket.send(sendPacket);

To receive a response, a receive packet is created and used with the receive method in the same way that it was handled in the server. This method will block until the server responds, and then the message is displayed:

byte[] receiveMessage = new byte[1024];

DatagramPacket receivePacket = new DatagramPacket(

receiveMessage, receiveMessage.length);

clientSocket.receive(receivePacket);

String receivedSentence =

new String(receivePacket.getData());

System.out.println(“Received from server [“

+ receivedSentence + “]\nfrom “

+ receivePacket.getSocketAddress());

Now, let’s see these applications at work.

The UDP client/server in action

The server is started first. It will display the following message:

UDP Server Started

Next, start the client application. It will display the following message:

UDP Client Started

Enter a message:

Enter a message, such as the following one:

Enter a message: Top of the morning to you

techbinz 6

[ 147 ]

The server will display that it has received the message, as shown next. You will see

several empty lines of output. This is the content of the 1024-byte array that is used

to hold the message. The message is then echoed back to the client:

Received from client: [Top of the morning to you

]

From: /127.0.0.1

On the client side, the response is displayed. In this example, the users then enter

“quit” to terminate the application:

Received from server [Top of the morning to you

]

from /127.0.0.1:9003

Enter a message: quit

UDP Client Terminating

As we are sending and receiving test messages, we can simplify the display of the message using the trim method when the message is displayed, as shown next. This code can be used on both the server and the client side:

System.out.println(“Received from client: [“

+ message.trim()

+ “]\nFrom: ” + receivePacket.getAddress());

The output will be easier to read, as shown here:

Received from client: [Top of the morning to you]

From: /127.0.0.1

This client/server application can be enhanced in a number of ways, including the use of threads, to enable it to work better with multiple clients. This example illustrates the basics of developing a UDP client/server application in Java. In the next section, we will see how channels support UDP.

Channel support for UDP

The DatagramChannel class provides additional support for UDP. It can support nonblocking interchanges. The DatagramChannel class is derived from the SelectableChannel class that makes multithreaded application easier. We will examine its use in techbinz 7, Network Scalability. The DatagramSocket class binds a channel to a port. After this class is used, it is no longer used directly. Using the DatagramChannel class means, we do not have to use datagram packets directly. Instead, data is transferred using an instance of the ByteBuffer class. This class provides several convenient methods to access its data. To demonstrate the use of the DatagramChannel class, we will develop an echo server and client application. The server will wait for a message from a client, and then send it back to the client.

The UDP echo server application

The UDP echo server application declaration follows and uses port 9000. In the main method a try-with-resources block opens the channel and creates a socket. The DatagramChannel class does not possess public constructors. To create a channel, we use the open method, which returns an instance of the DatagramChannel class. The channel’s socket method creates a DatagramSocket instance for the channel:

public class UDPEchoServer {

public static void main(String[] args) {

int port = 9000;

System.out.println(“UDP Echo Server Started”);

try (DatagramChannel channel = DatagramChannel.open();

DatagramSocket socket = channel.socket();){

}

}

catch (IOException ex) {

// Handle exceptions

}

System.out.println(“UDP Echo Server Terminated”);

}

}

techbinz 6

[ 149 ]

Once created, we need to associate it with a port. This is done first by creating an instance of the SocketAddress class, which represents a socket address. The InetSocketAddress class is derived from the SocketAddress class and implements an IP address. Its use in the following code sequence will associate it with port 9000. The DatagramSocket class’s bind method ties this address to the socket:

SocketAddress address = new InetSocketAddress(port);

socket.bind(address);

is created with the allocateDirect method. This method will attempt to use native

OS support directly on the buffer. This can be more efficient than using the datagram packet approach. Here, we created a buffer with the maximum size possible: ByteBuffer buffer = ByteBuffer.allocateDirect(65507);

Add the infinite loop that follows, which will receive a message from a client, display the message, and then send it back:

while (true) {

// Get message

// Display message

// Return message

}

The receive method is applied against a channel to get a client’s message. It will block until the message is received. Its single argument is the byte buffer that is used to hold the incoming data. If the message exceeds the size of the buffer, the extra bytes are silently thrown away.

The flip method enables the buffer to be processed. It sets the buffer’s limit to the current position in the buffer and then sets the position to 0. Subsequent get type methods will start at the beginning of the buffer:

SocketAddress client = channel.receive(buffer);

buffer.flip();

While not necessary for an echo server, the message that is received is displayed on the server. This allows us to verify that the message was received and suggests how messages can be modified to do more than simply echoing the message. In order to display the message, we need to use the get method to get each byte and then convert it to the appropriate type. The echo server is intended to echo simple

strings. Thus, the byte needs to be cast to a char before it is displayed.

However, the get method modifies the current position in the buffer. We need to restore the position to its original state before we send the message back to the client. The buffer’s mark and reset method are used for this purpose. All of this is performed in the following code sequence. The mark method sets the

mark at the current position. A StringBuilder instance is used to recreate the string that was sent by the client. The buffer’s hasRemaining method controls the while loop. The message is displayed and the reset method restores the position to the previously marked value:

buffer.mark();

System.out.print(“Received: [“);

StringBuilder message = new StringBuilder();

while (buffer.hasRemaining()) {

message.append((char) buffer.get());

}

System.out.println(message + “]”);

buffer.reset();

The last step is to send the byte buffer back to the client. The send method does this.

A message indicating that the message has been sent is displayed, followed by the

clear method. This method is used because we are through with the buffer. It will

set the position to 0, set the limit of the buffer to its capacity, and discard the mark:

channel.send(buffer, client);

System.out.println(“Sent: [” + message + “]”);

buffer.clear();

When the server is started, we will see a message to this effect, as shown here:

UDP Echo Server Started

Leave a Reply