UDP, TFTP, and IP Multicast in Java
I was recently working on a Java-programming project that involved some inter-process and inter-hardware communication. Unfortunately, since the client wanted to replicate the exact behavior of an older system with the software I was writing, we were not able to use the current industry standard TCP/IP sockets for communication. Instead, the specs called for a more complex communications protocol using UDP networking, TFTP, and IP Multicast. Getting the software working in Java involved some research, as well as some trial and error, so I thought I would write an article to give anyone else in the same situation the chance to avoid some of the work I did.
So, this article is a basic outline of how to get UDP, TFTP, and IP Multicast working in Java, along with some basic information about the three protocols and suggestions for further reading. It is intended for software developers who have at least intermediate knowledge of the Java language and Java development; background-level knowledge of TCP/IP will also be helpful for understanding some concepts. Since both TFTP and IP Multicast are based on UDP, I suggest that all readers start with the introduction and UDP sections; however, the TFTP and IP Multicast sections do not depend on each other, and either or both can be skipped if desired. I have also provided some fairly basic (but working) sample Java classes for free download.
Comments on this article are always welcome! If you have suggestions, questions, or any other comments, please contact Poplar ProductivityWare.
Contents of this article:
- Introduction to UDP, TFTP, and IP Multicast
- Adding UDP to a Java Application
- Adding IP Multicast to a Java Application
- Adding TFTP to a Java Application
- Download Sample Java Classes (bottom of screen)
Introduction to UDP, TFTP, and IP Multicast
UDP (User Datagram Protocol), like TCP, is a general-purpose communications protocol for inter-process and inter-computer communications that runs on top of IP (Internet Protocol). The details of TCP and UDP are quite different, however. One difference between TCP and UDP is that TCP has more hand-shaking and error checking built into the protocol, and a guarantee that data packets arrive in the correct order. Another difference is that instead of starting the communications process by negotiating to open a socket directly between two processes as in TCP, in UDP one process simply starts sending to a particular port at a particular IP address, while another one begins receiving, and thus communication is established. There is also some terminology that is different between the two protocols: TCP sends "packets" through its sockets, and UDP sends "datagrams" or "datagram packets". The Wikipedia entry on UDP is a good starting point for further reading on UDP.
TFTP (Trivial File Transfer Protocol) is a protocol that uses UDP to transfer files from one computer to another (much as the more familiar FTP uses TCP to transfer files). Like FTP, TFTP consists of a server-side application and a client-side application, but TFTP is a much simpler protocol with only minimal error checking built in. The server basically waits for a client to contact it on a specified port with a particular UDP datagram containing information about the file transfer request, and then the server sends (or receives) the datagrams containing chunks of the file, with an acknowledgement exchange after each packet. Technical details of the TFTP protocol specification can be found in RFC 1350 (1992), and the Wikipedia article on TFTP has more general background information.
IP Multicast is a protocol that uses UDP for efficient broadcast of messages to multiple receiving applications on multiple hosts. The broadcasting application sends its messages to an IP address in a particular reserved range, and applications on other computers on the same network listen to the same port on the same address to receive the message. In between, assuming that both the sending and receiving computers, as well as the network, have IP Multicasting installed, an efficient mechanism is used to send minimal copies of the message through the network. Note that the "message" sent can be any type of data -- it doesn't have to be text. The Wikipedia article on IP Multicast is a good starting point for further reading.
Adding UDP to a Java Application
In Java, the basic classes for UDP networking are java.net.DatagramSocket and java.net.DatagramPacket; you will probably also need other classes in the java.net package, such as java.net.InetAddress. These classes are provided by Sun as part of the standard J2SE distribution, and their full documentation can be found in the standard Sun Javadoc API documentation Use of these classes to do communications by UDP between two processes (a "sender" and a "receiver") is fairly straightforward. Here are the steps:
- Decide on a port for communication, and figure out the IP address of the receiving computer (which can be "localhost" if the two communicating processes are on the same computer). For this illustration, assume that we want to communicate on port 7777, and the receiving computer is at IP address 18.104.22.168.
- Create new instances of
java.net.DatagramSocket in both the
sender and receiver process.
It might seem like you should try to create a socket with a port and
address on it, but I have never gotten this to work without an exception
being thrown. So my advice is to use the no-argument default constructor on
the sending side, and
put the port and address on the packet (see below). On the receiving side,
you'll need to construct a socket with the desired port.
Code for this:
DatagramSocket sock = new DatagramSocket();
DatagramSocket sock = new DatagramSocket(7777);
- Create a new instance of
java.net.DatagramPacket in the sending process,
containing the information you want to send, and
using a constructor that contains the IP address and the port. For instance,
if you want to send a message contained in a byte array called "buf",
here is the code to create the packet:
DatagramPacket pack = new DatagramPacket(buf, buf.length, InetAddress.getByName("22.214.171.124"), 7777);
- Create a new instance of
java.net.DatagramPacket in the receiving process,
to receive the data; note that
if the sent information exceeds the size of the receiving buffer, the
incoming data will be truncated:
byte buf = new byte[LARGE_SIZE];
DatagramPacket pack = new DatagramPacket(buf, buf.length);
- Set up the receiving side to receive on the socket -- it will wait
until information comes in (or forever if no information arrives):
sock.receive(pack);Note that only one process can be receiving/listening on a given port at the same time, on the same computer.
- Send the datagram through the socket from the sender side:
- Once the receive completes, you can do something with the
information on the receiving side. Then, if you have more information to
receive, reset the length on the receiving
datagram packet, so that it is ready to receive a full-length message
the next time:
The UDPSender and UDPReceiver classes in the download file (below) are implementations of the above steps.
Adding IP Multicast to a Java Application
IP Multicast in Java requires only a small amount of coding beyond the basic UDP coding described above. In fact, there are really only a couple of extra steps:
- Choose an IP address that is in the range set aside for IP Multicast: 224.x.x.x - 239.x.x.x, with some addresses in that range reserved for specific purposes; for this example, we'll use 126.96.36.199.
- On the receiving side, use the java.net.MulticastSocket class when creating the socket (the sending side still uses the standard java.net.DatagramSocket).
- On the receiving side, after creating the socket, and before calling
sock.receive, use the
to join the IP Multicast group on the address you have chosen:
sock.joinGroup( InetAddress.getByName( "188.8.131.52" ));
The IPMulticastSender and IPMulticastReceiver classes in the download file (below) are implementations of the above steps.
For further reading, I recommend these two articles on IP Multicast programming:
- Multicasting in Java article from the Java Specialists' Newsletter
- IP Multicast general information, including sample code in C
Adding TFTP to a Java Application
In principle, you could get TFTP working in Java via the exact same mechanisms used above. However, TFTP requires very specific packets to be sent back and forth, with various pieces of header information, so it is easier to create TFTP applications in Java by using the Apache Jakarta Commons Net classes.
Note: When downloading the Commons Net classes for use in TFTP, please be aware that there is a serious bug in the TFTPClient class in the 1.41 release (the current release as of September 2006, when this article was written). Check the JIRA bug database for the Apache project (which you can find from the Apache Jakarta Bugs Page) and search for TFTPClient for more information; the bug caused the last packet of a sent file to be dropped. Also, although the JIRA bug database (more up-to-date than Bugzilla) states the bug has been fixed in the overnight build source code, I do not think that is the case. So, in order to get TFTP working, until they get it fixed on their end, my suggestion is: (a) download the source code for the stable release, (b) pick up my download file (below) and substitute the TFTPClient.java file from it into the source code, and (c) build the JAR file for the package and use it instead of the released JAR file (you will probably see some build errors and warnings, but none of them seem to apply to TFTP).
Anyway, assuming that you have a working version of the Apache Jakarta Commons Net package, here are the steps needed to add a TFTP client to your application:
- Create a new instance of
using the default constructor, and open a connection to the designated
TFTP port (normally, TFTP uses port 69, but as long as your client and
server agree, you can use a different port):
TFTPClient client = new TFTPClient();
- Use the TFTPClient.sendFile and TFTPClient.receiveFile methods to send files to and receive files from your TFTP server. See the Javadoc API documentation for the Commons Net package for more information on these methods.
If you need to add a TFTP server to your application, it is more complex, since the Commons Net package does not contain a class that acts as a TFTP server. So, you will have to duplicate the proper TFTP protocol behavior yourself, using some of the Commons Net classes. Here are the steps:
- Choose a port for your TFTP server. The default port is 69, but you may not have permission to use low-numbered ports for custom applications on your server, so you might have to choose a higher port number (2269, for instance).
- Create a new instance of
using the default constructor, and open a connection to the chosen
TFTP server = new TFTP();
- Check for incoming TFTP client connections using TFTP.receive(). If an incoming connection occurs, the method will return an org.apache.commons.net.tftp.TFTPPacket object. If timeout occurs before an incoming connection happens, the method will throw either a java.net.SocketException or a java.io.InterruptedIOException, and you can check again (looping until a packet is received).
- Check to see what type of packet was received -- it should be either a "read request" or "write request" packet, if the client is obeying the TFTP protocol.
- A "read request" packet means that the client wants you
to send a file. To do that, you'll need to:
- Break the file up into chunks of 512 bytes.
- Put each chunk into an org.apache.commons.net.tftp.TFTPDataPacket object; note that each packet must be sequentially numbered and contain the full number of bytes. The last packet has a smaller number of bytes (or zero if the file had exactly an even multiple of bytes), and signals the client that file transmission is complete.
- Send the packet using the TFTP.send() method
- Use TFTP.receive to wait for an acknowledgement packet from the client.
- After acknowledgement is received with the correct packet number, send the next packet.
- A "write request" packet means that the client wants to send you a file.
To do that:
- Start by sending an acknowledgement packet to the client (using org.apache.commons.net.tftp.TFTPAckPacket), with block number 0. This will signal the client that you are ready to receive the file.
- Wait for data packets to come in, by using TFTP.receive, sending an acknowledgement after each one, and saving the data from each. A packet with less than the maximum number of bytes signals the end of the file.
- There are some other details of the protocol that you might also want to implement. For instance, if you get something unexpected from the client, you can send them an error packet describing what happened, and if an expected packet (acknowledgement or data) in the sequence is missed, you can signal the client by re-sending the previous packet from your end. Read the RFC containing the TFTP specifications for more information. You may also find it instructive to read the source code for the TFTPClient class (which you can download from Apache -- see above), since a TFTP client goes through a similar sequence of data sending and receiving.
The TFTPServerApp and TFTPClientApp classes in the download file (below) are implementations of the above steps.
Download Sample Java Classes
To illustrate all of the networking examples above, I have created sample Java classes, which you can download below here in a zip file. The file size is 90.7 KB (92,973 bytes), and it includes public domain Java J2SE 5.0 source code, Javadoc, jar file, a fix for the Apache TFTP bug described above, and a "Readme" file.