Java Networking Tutorial

Chapter One - UDP


1. Introduction

This chapter introduces you to simple udp client/server programming. The first section discusses a java client communicating with the standard echo server at port 7. The second section discusses a java implementation of the echo server, and a small modification to the client to make it communicate with it.


1.1 Java Echo Client

This section introduces you to a simple udp client talking to the standard echo server at port 7. The client opens a udp connection to the host given as the first argument, and sends the message given as the second argument. The message is echoed by the server, and receieved and displayed by the client.

Listed below is the full source code of the java client.

// udpClient.java - Sends a message to the standard echo server

import java.io.*;
import java.net.*;

public class udpClient {
        
    public static void main(String argv[]) {

        String host = argv[0];
        String msg = argv[1];

        try {
            // set up a datagram connection with the server, 
            // false indicates a datagramsocket should be used.
            // port 7 is the standard echo server
            Socket sd = new Socket(host, 7 , false);

            // get the input- and outputstreams from the socket
            InputStream in = sd.getInputStream();
            OutputStream out = sd.getOutputStream();

            // create and fill a buffer to be sent and received
            byte[] buffer = new byte[256];
            msg.getBytes(0,msg.length(), buffer, 0);

            // send the buffer to the server through the outputstream
            out.write(buffer,0,msg.length());
            out.flush();

            // read the echoed message from the inputstream
            int length = in.read(buffer);
            // and print it, creating a new String from the buffer
            System.out.println("echo: "+new String(buffer,0,0,length));

        } catch (IOException e) {
            // Something went wrong with the socket connection
            System.out.println(e.toString());
        }
    }
}

An example run of the java client from the host defiant communicating with the echo server at the host enterprise:

	defiant:>java udpClient enterprise hello
	echo: hello

Let us discuss the client code in detail part for part.

import java.io.*;
import java.net.*;
The java.io package is imported because we need the InputStream and OuputStream classes, while the java.net package is imported because the Socket class is needed.
    public static void main(String argv[]) {

        String host = argv[0];
        String msg = argv[1];
The command line arguments (hostname and message) is put into the two variables host and msg.

        try {
            // set up a datagram connection with the server, 
            // false indicates a datagramsocket should be used.
            // port 7 is the standard echo server
            Socket sd = new Socket(host, 7 , false);
The first thing the client does is to open a socket to the server, using the Socket{} class. The Socket() constructor takes the hostname and the port number as the two first parameters. Port number 7 is used here which is the standard port for the echo server. The third parameter false indicates that this is a datagram socket, while true would specify a stream socket connection. The try{} statement is explained later.
            // get the input- and outputstreams from the socket
            InputStream in = sd.getInputStream();
            OutputStream out = sd.getOutputStream();
Before any communication can occur on a datagram socket, an input-stream and output-stream has to be created from the socket. This is done using the getInputStream() and getOutputStream() methods of the Socket class. These two functions return objects of the class InputStream{} and OutputStream{}, which can be read and written to by the write() and read() methods explained later. Here the variables in and out are used to represent the two streams.
            // create and fill a buffer to be sent and received
            byte[] buffer = new byte[256];
            msg.getBytes(0,msg.length(), buffer, 0);
An ordinary output-stream can only send a byte or an array of bytes. We therefore create a buffer of bytes from the message we got from the command line, so that it can be written to the ouput-stream.

We first create a buffer of 256 bytes. Then the String method getBytes() is used to place the string msg into the buffer. The first two parameters represent the start and end position of the source string to be copied. We want the whole string to be copied so we use 0 and the length of the string. The third parameter is the buffer to be filled, while the last one is the start position of the destination buffer.

            // send the buffer to the server through the outputstream
            out.write(buffer,0,msg.length());
            out.flush();
The buffer is then written to the output stream using the write() and flush() OutputStream methods. The first parameter of the write method is the buffer to be sent, the second and last parameter are the offset of the buffer, and the number of bytes to send. Here the portion of the buffer containing the message is sent.
            // read the echoed message from the inputstream
            int length = in.read(buffer);
            // and print it, creating a new String from the buffer
            System.out.println("echo: "+new String(buffer,0,0,length));
The client then blocks and waits for an echo from the server. This is done using the InputStream read() method. An array of bytes to place the received data in is the only parameter. The method blocks until some data is available, and returns the length of the data upon a succesful read. Here the same buffer as we used for sending is also used to receive the data. The data is then printed on standard output using System.out.println().

The data can not properly be printed as it is receieved so a string has to be constructed from the buffer. Here we use a string constructor which constructs a string from a buffer of bytes. The first parameter to the constructor is the buffer just received. All characters in java is represented in Unicode which uses two bytes, so we have to specify the high byte of every character in the new string. The second parameter is used for the value of this high byte and will in most cases be 0. The last two parameters are the offset and the length of the portion of the buffer to be converted.

        } catch (IOException e) {
            // Something went wrong with the socket connection
            System.out.println(e.toString());
        }
    }
}
Most of the I/O and Socket methods used in the above statements can fail. In java runtime failures are handled with exceptions. If you want to catch an exception you use the try{ ... } catch (exception) { ... } construct. The method call that can cause the exception is places in the try{} portion while the exception handling code is placed in the catch{} statement. Our exception handling here simply consists of printing out the exception to standard output and exiting the program.

To learn more about exceptions look here.


1.2 Java UDP Echo Server

This section shows you how to implement a udp echo server yourself in java. The classes DatagramSocket{}, DatagramPacket{}, and InetAddress{} are introduced.

The server first opens a datagram socket on an arbitrary port. It then creates a datagram packet to receive data in, and waits for input from the socket. When a packet is received, a new one is created with the address of the sender as the destination address. The received data is then put into the package and sent back to the client.

Listed below is the full source code of the java echo server.

// udpServer.java 

import java.io.*;
import java.net.*;

public class udpServer {
	
    static final int size = 256;

    public static void main(String argv[]) {

        DatagramSocket sd = null;
        DatagramPacket packet = null;

        try {
            // create a datagram socket
            sd = new DatagramSocket();
            // print the chosen port number
            System.out.println("Server waiting on port: "+sd.getLocalPort());
	
            for (;;) {
                // create a datagram packet to receive message in
                byte[] buffer = new byte[size];
                packet = new DatagramPacket(buffer, size);

                // wait for message from client
                sd.receive(packet);
			
                // extract information from datagram packet
                byte[] data = packet.getData();
                int port = packet.getPort();
                int length = packet.getLength();
                InetAddress address = packet.getAddress();

		// print information to standard output
                String msg = new String(buffer,0,0,length);
                System.out.println("from "+address+":"+port+": "+msg);

                // create a datagram packet to send message in
                packet = new DatagramPacket(buffer, length, address, port);

                // send the message back to the client
                sd.send(packet);
            }

        } catch (SocketException e) {
            // Something went wrong with the socket connection
            System.out.println(e.toString());

        } catch (IOException e) {
            // Something went wrong on receive
            System.out.println(e.toString());
        }	
    }
}

An example run of the java echo server at the host enterprise with a modified client running at the host defiant:

Server:

	enterprise:>java udpServer
	Server waiting on port: 33223
	...
	from 128.82.7.66:33231: hello
Client:
	defiant:>java udpClient2 enterprise 33223 hello
	echo: hello
Let us examine the server in more detail.
import java.io.*;
import java.net.*;
The java.io package is imported because we need the SocketException and IOExcption classes, while the java.net package is imported because the DatagramSocket, DatagramPacket, and InetAddress classes are needed.
public class udpServer {
	
    static final int size = 256;

    public static void main(String argv[]) {

        DatagramSocket sd = null;
        DatagramPacket packet = null;

        try {
            // create a datagram socket
            sd = new DatagramSocket();
            // print the chosen port number
            System.out.println("Server waiting on port: "+sd.getLocalPort());
The first thing the server does is to open a datagram socket of type DatagramSocket{}. The DatagramSocket() constructor creates a socket on an arbitrary port. To create a socket on a specified port use the constructor DatagramSocket(int). The chosen port number is then printed to standard output using the DatagramSocket getLocalPort() method. A datagram socket has its own methods for sending and receiving data, so you don't have to create specific input- and output-streams.

Since datagram communication is connection-less all messages sent have to contain the address of the destination. A message sent and received over a DatagramSocket is of type DatagramPacket{}. This packet contains the data, address, and port number of the source or destination, depending on the packet being sent or received.

            for (;;) {
                // create a datagram packet to receive message in
                byte[] buffer = new byte[size];
                packet = new DatagramPacket(buffer, size);
Before constructing a datagram packet, an array of bytes to place the data in has to be created, and given to the constructor. The create a datagram packet to be received you use the constructor DatagramPacket(buffer,length), which specifies the data buffer, and the number of bytes to be received as its parameters. The constructor for creating a "sending" datagram packet is explained further down.
                // wait for message from client
                sd.receive(packet);
The server then blocks and waits for a datagram packet using the DatagramSocket receive() method, taking the newly created datagram packet as a parameter. The packet received will contain the data, source address and port number, and the length of the data.
                // extract information from datagram packet
                byte[] data = packet.getData();
                int port = packet.getPort();
                int length = packet.getLength();
                InetAddress address = packet.getAddress();
The information mentioned above is then extracted from the packet. The data is copied into an array of bytes using the getData() method, while the port number and lenght of the data is copied into integer variables using the getPort() and getLength() methods. The source address is then copied into a variabel of type InetAddress{}, using the DatagramPacket getAddress() method.

The information is then printed to standard output as show below.

		// print information to standard output
                String msg = new String(buffer,0,0,length);
                System.out.println("from "+address+":"+port+": "+msg);
At present the class DatagramPacket lacks methods for setting the different attributes printed above. Because of this a brand new packet has to be created.
                // create a datagram packet to send message in
                packet = new DatagramPacket(buffer, length, address, port);
The constructor DatagramPacket(buffer,length,address,port) is used to create the new datagram packet to be sent. As with the other constructor this also needs to place the data to be sent in an array of bytes. Since we are just returning the data we just received, we use the buffer we copied from the received packet. The length of the data is then set as the second parameter. What specifies this as a "send" packet is the last two arguments, the address and port number of the destination. We are just returning the packet to the sender, so we give the address and port which was copied from the received packet as parameters.
                // send the message back to the client
                sd.send(packet);
            }                       
The packet is then sent back to the client using the DatagramSocket send() method.
        } catch (SocketException e) {
            // Something went wrong with the socket connection
            System.out.println(e.toString());

        } catch (IOException e) {
            // Something went wrong on receive
            System.out.println(e.toString());
        }	
    }
}
The catch{...} part of the try{...} statement catches SocketExceptions and IOExceptions of the socket(), read(), and write() calls, and prints the exception to standard output.


1.2 A Generic Java UDP Client

The client from section 1.1 could only contact the standard echo server. Here is a modified version of that client. The modified code of the client is briefly described below.
        String host = argv[0];
        int port = (new Integer(argv[1])).intValue();
        String msg = argv[2];

        try {
            // set up connection with the server, false 
            // indicates a datagramsocket is to be used.
            Socket sd = new Socket(host, port , false);
Instead of always contacting port number 7, we give the port number to connect to as a command line argument (argument #1). All command line arguments are represented as strings, so the port number has to be converted to an int before it can be used as a parameter for the socket constructor.

The string represented port number is converted by first creating an object of type Integer{}, and then using the method intValue() on this object which returns the number as an int.

The port number, now represented as an int, is then used to create the new socket.


Last updated 3/23/96 by kvande@cs.odu.edu

HomePage