Networking (UDP / TCP)

UDP: Greeting Server (1 session)

In this session, we you will explore the API for receiving and sending UDP message in Java.

Reference Code & Quiz

You are given the following example code that both send and receive UDP messages.

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

public class Net {

    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(4445);
        boolean running = true;
        byte[] buf = new byte[256];

        while (running) {
            DatagramPacket inPacket  = new DatagramPacket(buf, buf.length);
            socket.receive(inPacket);
            String received = new String(inPacket.getData(), 0, inPacket.getLength());
            System.out.println("Received: " + received);

            if (received.equals("end")) {
                running = false;
                continue;
            }
            
            InetAddress senderAddress = inPacket.getAddress();
            int senderPort = inPacket.getPort();
            DatagramPacket outPacket = new DatagramPacket(buf, buf.length, senderAddress, senderPort);
            socket.send(outPacket);
        }
        socket.close();
    }
}

By analyzing the code sample above, answer all the following questions:

Reception:

  • Which lines are responsible for receiving messages?
  • On which port is the server listening?
  • What is the maximum size of a received message?
  • What does the server do when a message is received?

Sending:

  • What are the lines responsible for sending messages?
  • What is the content of the message sent?
  • Where is the message sent? (which address and port)

Miscellaneous:

  • What class represents an IP adress?
  • Under which conditions does the program terminates?
  • What are the blocking operations in this code?

Hands-On exercises

Exercise: Write a Java program that:

  • listens on a given port for incoming UDP connections
  • when receiving the message "XXXX" prints "Greetings, XXXXX" on the standard output (i.e. in the terminal where the server is running)

For testing your program, you can use the following command to send UPD message to a given port (here 1789). Note that this will only work on linux.

# Send a UPD message (here "Alice"), on a given port (here 1789) 
# on a given address (here localhost: 127.0.0.1)
echo -n "Alice" > /dev/udp/127.0.0.1/1789

Exercise: Instead of using the command line, create a new Java program that sends a message to the server. Note that you can construct an InetAddress from a string with the InetAddress.getByName() method (e.g. InetAddress.getByName("127.0.0.1")).

Exercise: Now we will combine all the code you have written into a single program.

  • Adapt your server side code (the one that listens for new UDP messages) so that it is implemented in a GreeterServer class that extends Thread (the run() method of the thread should execute the server)
  • in the same program, create a sendName(String name) method that send a name String to the server (essentially doing the same as your client code from earlier)
  • In the main method, start a greeter server thread
  • In the same main, use the sendName method to send several messages to the server.

Exercise: In addition to the name of the person, print the IP address from where the message was sent. Check that this work by sending a message from another machine (suggestion: use SSH and the echo command above)

TCP (1 session)

Your objective in this session is to write an averaging service over TCP.

In a nutshell, it means that you will have a server running to which any user can connect. Once a user is connected, it may start sending numbers (integers) to the server. Each time the user sends a new number, the server must answer with the average of the number received so far from this user.

Example of what a session should look like:

Welcome to the averaging client.
Please enter the next number.
> 6
Sending to server awaiting response...
Received current average: 6.0 (computed on our HPC cluster)
Please enter the next number.
> 9
Sending to server awaiting response...
Received current average: 7.5 (computed on our HPC cluster)

Note that:

  • multiple users must be able to connect to your server at the same time
  • you must not mix the numbers of two users

Ready? Code! (but you may want to look at the resources below ;)

Useful Resources for this exercise

The tutorial below introduces all concepts that are useful for you to know for this exercise. Take the time to read it carefully.

Reading/Writing Data to Streams

The Java standard library provides abstractions for input/output with two main classes:

  • InputStream: anything you can read from (standard input, files, incoming sockets, ...)
  • OutputStream: anything you can write to (standard output, files, outgoing sockets, ...)

These two classes are fairly low-level and allow you to read/write bytes. Since this is usually to low-level, the standard library provides many wrappers around those classes that extend their capabilities.

In particular, useful for today's exercise are the DataOutputStream and DataInputStream classes. When wrapping the input/output streams of a socket, they allow you to transfer common java primitives (ints, doubles, ...).

Below are two examples for reading/writing double to sockets.

static void writeDoubleToSocket(Socket socket, double number) throws IOException {
    DataOutputStream out = new DataOutputStream(socket.getOutputStream());
    out.writeDouble(number);
}

static double readDoubleFromSocket(Socket socket) throws IOException {
    DataInputStream in = new DataInputStream(socket.getInputStream());
    return in.readDouble();
}

Reading from standard input

Below is an example code that reads an integer from the console (standard input). In Java, the standard input is accessible through the System.in object (of type... InputStream).

/** Reads an integer from the standard input and returns it.
 *  
 *  If the user enters an invalid text, he will be prompted to retry.
 */
static int readIntFromConsole() throws IOException {
    // System.in is an InputStream which we wrap into the more capable BufferedReader
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    while (true) {
        String input = reader.readLine();
        try {
            int n = Integer.parseInt(input);
            return n;
        } catch (NumberFormatException e) {
            System.out.print("Not an integer, try again: ");
        }
    }
}