CA597 Tutorial - Client/Server System using Sockets and Datagrams


Content:
We will implement a client/server system. This is similar to the previous example, only that we use datagram packets instread of datastreams here. Again the connection will be established based on sockets. Another difference to the previous client/server system is, that we use threads on the server side here. Using threads allows the server to deal with several clients at the same time. This is example is based on one of the programs from the Java Tutorial by Campione and Walrath. I have simplified their version. You can find the full version online here.

Instead of explaining the functionality communication-oriented, I will describe each class in separation here. But we start with defining what a datagram is.

Datagrams

A datagram is an independent, self-contained message sent over the network whose arrival, arrival time, and content are not guaranteed. This indicates that the UDP protocol is used. For more details see CA587 Networks and Internet or the explanations in the Java tutorial.

The Applet (Client)

Essentially the applet sends a request (an empty packet) to the server and receives a reply (a number). The number will then be displayed.

In the initialisation the applet determines its host:

	String host = getCodeBase().getHost();

        address = InetAddress.getByName(host);
(I have omitted the exception handling.) The idea is here that the server runs on the same machine. The applet will try later on to connect to that address.

Then, a datagram socket is created:

	socket = new DatagramSocket();
The next steps involve setting up the interface consisting of a label, a textfield (the port number the server is listening to has to be entered here) and a Send button (which sends the initial request to the server).
	display = new Label("No number");
        add(display);

        portField = new TextField(6);
        add(portField);

        Button button = new Button("Send");
        add(button);

        validate();
validate makes sure that all GUI components are actually displayed (this is not necessary for most of the existing browsers).

In case the Send button is pressed, the action method is invoked (this is an outdated construct, you can replace it by action listeners). Before the applet tries to connect to the server, the portnumber which should have been typed into the textfield is syntactially checked. In case the port number is an integer, the method proceeds and calls the connect-method:

    public boolean action(Event event, Object arg) {
        int port;

        try {
            port = Integer.parseInt(portField.getText());
        } catch (NumberFormatException e) {
            return true;
        }

        connect(port);
        return true;
    }
The connect-method creates an empty datagram packet first:
	DatagramPacket packet;
        byte[] sendBuf = new byte[256];

        packet = new DatagramPacket(sendBuf, 256, address, port);
which is subsequently send to the server via the datagram socket declared earlier on.
	socket.send(packet);
Then, a new packet is defined and the applet waits for the server to reply:
	packet = new DatagramPacket(sendBuf, 256);

	socket.receive(packet);
The server sends an integer value, which is converted into a string and displayed as a label:
        String received = String.valueOf((int)(packet.getData())[0]);
        display.setText(received);

The Server

The server class is quite simple. It just starts the thread:
class NetServer {
    public static void main(String[] args) {
        new NetServerThr().start();
    }
}

The Server Thread

The thread - once started - will run continuously. At the beginning it initialises itself with the name NetServer.
     NetServerThr() {
        super("NetServer");
And it declares a datagram socket (as the applet):
	private DatagramSocket socket = null;
	
	socket = new DatagramSocket();
The it prints out to the screen at which port it is listening:
	System.out.println("NetServer listening on port: " +
                           socket.getLocalPort());
This number has to be typed in into the textfield in the applet!

The run-method provides the implementation of the thread. It stops if an unvalid socket was created:

	if (socket == null)
            return;
In a loop, the thread will continuously wait for incoming requests:
	while (true) {
	...
	}
A packet is created in which incoming data is received:
	packet = new DatagramPacket(buf, 256);
        socket.receive(packet);
Address and port of the client are stored:
	address = packet.getAddress();
        port = packet.getPort();	

The Server implements a counter. That means for each request from any client, it sends back an incremented number.

i = i + 1;
The integer value is converted into a byte and stored as the first element of the buf-array. buf is the content of the datagram packet. The packet is send back to the client.
	buf[0] = (byte)i;
        packet = new DatagramPacket(buf, 1, address, port);
        socket.send(packet);

At the end, when the server is shut down, the socket connection will be closed.

    protected void finalize() {
        if (socket != null) {
            socket.close();
            socket = null;
        }
    }

Client and Server Code

Here is the full text of the applet:
import java.applet.Applet;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class NetApplet extends Applet {
    boolean DEBUG = true;
    InetAddress address;
    TextField portField;
    Label display;
    DatagramSocket socket;

    public void init() {
        String host = getCodeBase().getHost();

        try {
            address = InetAddress.getByName(host);
        } catch (UnknownHostException e) {
            System.out.println("Couldn't get Internet address: Unknown host");
        }

        try {
            socket = new DatagramSocket();
        } catch (SocketException e) {
            System.out.println("Couldn't create new DatagramSocket");
            return;
        }

        display = new Label("No number");
        add(display);

        portField = new TextField(6);
        add(portField);

        Button button = new Button("Send");
        add(button);

        validate();
    }

    void connect(int port) {
        DatagramPacket packet;
        byte[] sendBuf = new byte[256];

        packet = new DatagramPacket(sendBuf, 256, address, port);

        try { // send request
            if (DEBUG) {
                System.out.println("Applet about to send packet to address "
                               + address + " at port " + port);
            }
            socket.send(packet);
            if (DEBUG) {
                System.out.println("Applet sent packet.");
            }
        } catch (IOException e) {
            System.out.println("Applet socket.send failed:");
            e.printStackTrace();
            return;
        }

        packet = new DatagramPacket(sendBuf, 256);

        try { // get response
            if (DEBUG) {
                System.out.println("Applet about to call socket.receive().");
            }
            socket.receive(packet);
            if (DEBUG) {
                System.out.println("Applet returned from socket.receive().");
            }
        } catch (IOException e) {
            System.out.println("Applet socket.receive failed:");
            e.printStackTrace();
            return;
        }

        String received = String.valueOf((int)(packet.getData())[0]);
        display.setText(received);
        if (DEBUG) {
            System.out.println("Result: " + received);
        }
    }

    public boolean action(Event event, Object arg) {
        int port;

        try {
            port = Integer.parseInt(portField.getText());
        } catch (NumberFormatException e) {
            //No integer entered.  Should warn the user.
            return true;
        }

        connect(port);
        return true;
    }
}
The server code is divided into a server program and the thread code. Here is the server program code:
class NetServer {
    public static void main(String[] args) {
        new NetServerThr().start();
    }
}
And here is the server thread code:
import java.io.*;
import java.net.*;
import java.util.*;

class NetServerThr extends Thread {
    private DatagramSocket socket = null;

    NetServerThr() {
        super("NetServer");
        try {
            socket = new DatagramSocket();
            System.out.println("NetServer listening on port: " +
                                socket.getLocalPort());
        } catch (java.net.SocketException e) {
            System.err.println("Could not create datagram socket.");
        }
    }

    public void run() {
        int i = 0;

        if (socket == null)
            return;

        while (true) {
            try {
                byte[] buf = new byte[256];
                DatagramPacket packet;
                InetAddress address;
                int port;
                String dString = null;

                    // receive request
                packet = new DatagramPacket(buf, 256);
                socket.receive(packet);
                address = packet.getAddress();
                port = packet.getPort();

                    // send response
                i = i + 1;
                buf[0] = (byte)i;
                packet = new DatagramPacket(buf, 1, address, port);
                socket.send(packet);
            } catch (Exception e) {
               System.err.println("Exception:  " + e);
                e.printStackTrace();
            }
        }
    }
    protected void finalize() {
        if (socket != null) {
            socket.close();
            socket = null;
            System.out.println("Closing datagram socket.");
        }
    }

}

How to Extend Your Applet

© Claus Pahl