CPSC 441, Fall 2014
Lab 5: Programming UDP
The assignment for this lab is to write a UDP server and a matching UDP client. The specifications are given below, along with some information about programming UDP in Java. You should submit your work into your homework folder in /classes/cs441/homework by next Friday.
To give you something definite to work on, you should write UDP client/server programs for looking up zip code information. The file /classes/cs441/zipcodes.txt contains the information that you need. Each line of the file has a zip code, a latitude, a longitude, a city name, and a state name. The five data fields are separated by commas. (This makes it easy to break a line into its five parts by using line.split(",").)
Your server program should begin by reading the file and storing the information that it contains in some kind of data structure. I suggest creating a simple class to hold the data for one zip code. Store the objects in a HashMap, using the zip code as a key. Depending on what exactly you want your server to do, you could use additional HashMaps or other data structures for looking up information based on other keys. (Since the file is quite long, I suggest that you read the file directly from its original location, rather than make a copy.)
Your server will process requests from clients in an infinite loop. The client will send a request that the server look up some zip code information, and the server will send back a response. For example, the client could send a numerical zip code and the server could send back the name of the city and state that uses that zip code, or an error message if the zip code does not exist. That would be a very minimal server. A more reasonable server would allow several types of queries. For example, if sent a city name, the server could return all zip codes for cities with that name.
In addition to the server, you should write a client program (or several client programs for different kinds of queries). The programs should make it reasonably easy for a user to query the zip code server.
You will need to design a protocol that the client and server can use for communication. This is an application-level protocol that you are making up, and the details are all up to you. Make sure that your protocol is well-documented in the comments on your server. Also be sure to include comments in the client program that explain how to use it.
UDP Programming in Java
You can check out http://docs.oracle.com/javase/tutorial/networking/datagrams/clientServer.html for the official tutorial on "Datagram" client/server programming in Java. I should leave it at that. However, I did write an example of my own, which you can find in the files Server.java and Client.java in the folder /classes/cs441/udp. And I will document some of the basic API that you need here. The main classes that you need to work with are DatagramSocket, DatagramPacket, and InetAddress. A DatagramSocket is used for sending and receiving UDP datagrams, which are represented by the DatagramPacket class. There is no distinction between client and server sockets; the difference is in how they are used. You have already seen the InetAddress class, which represents an IP address, in connection with TCP. Here are some API calls, with comments about how they are used.
new DatagramSocket()— This constructor creates a datagram socket that that is bound to some available port. This is mostly for use in clients, where you don't care about the local port number. Can throw SocketException.
new DatagramSocket(portNum)— Creates a datagram socket bound to a specified port number. This is mostly for use in servers. The socket can receive packets that are sent (by clients) to the specified port. Can throw SocketException.
new DatagramPacket(buffer, length, inetAddress, portNum)— Creates a datagram packet, probably for use with a socket's send method. The buffer is an array of bytes that contains the data to be sent, and the length is the number of bytes of data. The inetAddress is the address to which the packet will be sent, and the portNum is the port to which it will be send. This can be used in a client for a packet to be sent to a server; in that case, the address and port are the address and port on which the server is listening. (Use InetAddress.getByName() to look up the InetAddress for a host.) This constructor can be used in a server to send a response to the client; in that case, the inetAddress and portNum would come from the packet that was sent by the client to the server.
new DatagramPacket(buffer, length)— Creates a datagram packet with a specified size. The first parameter is an array of bytes and the second is the number of bytes that the packet can hold. (This can be equal to or less than buffer.length.) This would ordinarily be used with the socket's receive method. In that case, the buffer would not contain any data before the constructor is called. This can be used in a server to receive a packet from a client or in the client to receive a response back from the server.
datagramSocket.send(packet)— Sends a DatagramPacket. The recipient's IP address and port number should be in the packet, along with the data. This can throw an IOException, but remember that successfully sending the packet does not guarantee that it will be received.
datagramSocket.receive(packet)— Waits for a packet to be received over the network. The parameter is a DatagramPacket that will be filled with information about the packet, including the data that was received and the IP address and port number of the sender. (The address information is often used to construct a response packet.) This can throw an IOException. I don't know what happens if the packet that is received is too large to fit into the buffer that was allocated for the packet.
datagramSocket.setSoTimeout(milliseconds)— Sets a timeout for receive operations on the socket. If datagramSocket.receive() has not received a packet after the specified timeout period, then a SocketTimeoutException is thrown, aborting the receive operation. If this method has not been called before the receive operation, then the receive can block forever.
datagramPacket.getData()— This is just a pointer to the buffer that was supplied in the DatagramPacket constructor.
datagramPacket.getLength()— For a packet that was received over the net, this is the actual number of bytes that were received.
datagramPacket.getAddress()— For a packet that was received, this is the InetAddress of the sender. It is often used in the constructor for making a response packet.
datagramPacket.getPort()— For a packet that was received, this is the port number of the sender. It is often used in the constructor for making response packet.
InetAddress.getByName(host)— This static method returns an InetAddress for the specified host. The parameter is a string that can be either a host name or an IP address. Can throw an UnknownHostException.
str.getBytes()— For a String str, this function returns an array of bytes that represents the string. (The array can then be used as a buffer in a DatagramSocket.)
new String(buffer, startIndex, length)— Constructs a String from the contents of buffer, which is an array of bytes. The startIndex is the starting index for the data in the array, and is often zero. The length is the number of bytes in the data. (This can be used as a convenient way to get the data from a DatagramPacket as a string; use new String(packet.getData(),0,packet.getLength().)