Computer networks -- 2007-2008 -- info.uvt.ro/Laboratory 3

Important! These pages are somehow outdated and it is recommended to consult the newer version at Computer networks -- 2008-2009 -- info.uvt.ro (by Ciprian Crăciun).

Quick links:


Prerequisites

edit

The prerequisites necessary for this laboratory are:

References

edit

Differences between TCP and UDP

edit

As we have seen in the first laboratory:

  • TCP is a connection-oriented, stream-oriented, ordered, reliable protocol over IP;
  • UDP is a connection-less, datagram-oriented, unordered, unreliable protocol over IP;
  • also we could add that TCP is much slower than UDP because of the ordering and reliability;

So we could conclude that these two protocols are diametrically opposite.

UDP sockets

edit

Like in the case of TCP sockets we usually split the applications in two main categories: servers and clients.

But in this situation we do not have UDP client and server sockets, we speak only of UDP sockets -- as UDP is a connection-less protocol. Nonetheless in order for the communication to take place the same conditions -- like in the case of TCP -- must be met:

  • the server should listen on a certain IP address and port -- again the local socket address;
  • the client should know in advance the exact IP address and port of the server -- the remote socket address;
  • the client must also have a local socket address, just like in the case of the server;

Life-cycle

edit

Thus the generic life-cycle of an UDP socket would be:

  • creation -- the socket object is created;
  • binding -- the local socket address is established (IP plus port);
  • data exchange -- we shall see further what data exchange implies;
  • destruction -- the socket object is destroyed, and no longer used;

As we can see there is nowhere any connection being made, and thus both the client and the server act the same way, the only distinction is that usually the client is the one that initiates the dialog.

Data exchange

edit

The data exchange step is split in two actions:

  • sending a datagram:
    • datagram creation -- we create a datagram object for which we establish:
      • the data buffer -- what we want to send;
      • the destination socket address -- where do we want to send the datagram; this is composed of an IP and port of the other entity;
    • datagram sending -- we actually send the datagram over the network to the specified destination;
  • receiving:
    • datagram creation -- we create a datagram object for which we establish:
      • the buffer -- where we want to receive the data;
    • we wait for datagrams, and when one arrives we obtain it;
    • we must obtain the datagram length, in order to know how much of the buffer has been written;
    • we may also obtain the sender's socket address;

We could observe from the previous steps that the data exchange could be done with multiple entities, thus one client could speak with more than one server (or one server with multiple clients) by using the same socket -- something which in the case of TCP sockets is not possible.

Also we should observe that in the case of UDP sockets we must prepare in advance the buffer where we want to receive the data, and in the case buffer is to small to hold the entire received datagram, usually an error will be raised, and we can retry with a greater buffer.

"Connected" UDP sockets

edit

A second of the socket life-cycle is different in the fact that we introduce another step -- the connection step -- between the binding and the data exchange, and we remove the datagram remote socket address setup. This does not mean that we shall use a connection-oriented UDP socket. This is just an Java API facility which allows us to ignore the remote socket address when sending, provided that we communicate with only one server, thus no packet is sent during the connection step, and on the wire there is no difference between the two variants. So:

  • creation -- identical with the previous variant;
  • binding of the local socket address -- identical;
  • connection -- establishing the default remote address;
  • data exchange:
    • sending a datagram:
      • datagram creation:
        • data setup;
        • there is no remote address setup;
      • datagram sending -- identical;
    • receiving a datagram -- identical;
  • destruction;

Java API

edit

Examples

edit
  • creating and binding the socket:
DatagramSocket socket = new DatagramSocket ();
  • resolving the remote (destination) socket address (IP and port) -- this is usually done on the client side:
InetAddress remoteAddress = InetAddress.getByName (remoteAddressName);
int remotePort = Integer.parseInt (remotePortName);
SocketAddress remoteSocketAddress = new InetSocketAddress (remoteAddress, remotePort);
  • sending a datagram:
    • creating the datagram:
byte[] requestBuffer = requestString.getBytes ();
int requestOffset = 0;
int requestLength = requestString.length ();
DatagramPacket requestDatagram = new DatagramPacket (requestBuffer, requestOffset, requestLength, remoteSocketAddress);
    • sending the datagram:
socket.send (requestDatagram);
  • receiving a datagram:
    • creating the datagram:
byte[] responseBuffer = new byte [2048];
DatagramPacket responseDatagram = new DatagramPacket (responseBuffer, 0, responseBuffer.length);
    • waiting for a datagram:
socket.receive (responseDatagram);
    • obtaining the datagram data:
int responseOffset = responseDatagram.getOffset ();
int responseLength = responseDatagram.getLength ();
String responseString = new String (responseBuffer, responseOffset, responseLength);
    • obtaining the remote (source) socket address -- this is usually done on the server side:
SocketAddress senderSocketAddress = datagram.getSocketAddress ();
  • closing the socket:
socket.close ();

Java binary IO generic API

edit

The following API is used to handle binary data -- usually under the form of binary streams. Because there are a lot of methods available to the user, we shall list only the interface and class names.

Unicast vs. broadcast

edit

When sending messages we can distinguish three cases, based on the destination:

  • a single specified -- explicit -- destination => unicast;
  • all local -- implicit -- destinations => broadcast;
  • any local -- implicit -- destination => anycast;
  • all interested -- implicit -- destinations => multicast;

Observation: implicit and explicit refers to the fact that the sender must know the exact address of the involved destinations:

  • explicit means that we do need an exact address for the destination;
  • implicit means that we use as an destination a group address, and it's the job of the actual destination hosts to listen or join on this address;

In case of broadcast we can use as the IP address:

  • the local network broadcast address; for example if our IP is 10.10.10.8, and the netmask is 255.255.255.0, then the local network broadcast address is 10.10.10.255;
  • the global network broadcast address: 255.255.255.255;

Observation: if a host makes a broadcast, the sent packet is tagged with the its (the sender) IP address, and thus any receiving host can determine the source address.

Links:

Complete example

edit

In what follows we shall try to present a version of the example presented in the previous laboratory, but this time by using UDP sockets on both the client and server side: Computer networks -- 2007-2008 -- info.uvt.ro/Laboratory 2#Complete example. (Also the way to run the client and the server is the same.)

Client

edit

The source code for both the client and the server can be found inside the Subversion repository in the folder examples/example-03.

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;


public class Client
{
	public static void main (String[] arguments) throws Exception
	{
		// Checking the argument count
		if (arguments.length != 3) {
			System.err.println ("Bad usage.");
			return;
		}
		
		// Selecting the arguments
		String remoteAddressName = arguments[0];
		String remotePortName = arguments[1];
		String requestString = arguments[2];
		
		// Creating and binding the client socket
		DatagramSocket socket = new DatagramSocket ();
		
		// Resolving the server address and port
		InetAddress remoteAddress = InetAddress.getByName (remoteAddressName);
		int remotePort = Integer.parseInt (remotePortName);
		
		// Creating the remote socket address
		SocketAddress remoteSocketAddress = new InetSocketAddress (remoteAddress, remotePort);
		
		// Creating request datagram
		byte[] requestBuffer = requestString.getBytes ();
		int requestOffset = 0;
		int requestLength = requestString.length ();
		
		// Creating the request datagram
		DatagramPacket requestDatagram = new DatagramPacket (
				requestBuffer, requestOffset, requestLength, remoteSocketAddress);
		
		// Sending the request datagram
		socket.send (requestDatagram);
		
		// Creating the response buffer
		byte[] responseBuffer = new byte [2048];
		
		// Creating the response datagram
		DatagramPacket responseDatagram = new DatagramPacket (responseBuffer, 0, responseBuffer.length);
		
		// Waiting and receiving the response datagram
		socket.receive (responseDatagram);
		
		// Getting the response buffer positions
		int responseOffset = responseDatagram.getOffset ();
		int responseLength = responseDatagram.getLength ();
		
		// Creating the response string
		String responseString = new String (responseBuffer, responseOffset, responseLength);
		
		// Closing the socket
		socket.close ();
		
		// Printing the response
		System.out.println (responseString);
	}
}

Server

edit

As noted previously the source code for both the client and the server can be found inside the Subversion repository in the folder examples/example-03.

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;


public class Server
{
	public static void main (String[] arguments) throws Exception
	{
		// Creating and binding the server socket
		DatagramSocket socket = new DatagramSocket (new InetSocketAddress (InetAddress.getByName ("0.0.0.0"), 7654));
		
		// Creating the request buffer
		byte[] requestBuffer = new byte [2048];
		
		// Creating the request datagram
		DatagramPacket requestDatagram = new DatagramPacket (requestBuffer, 0, requestBuffer.length);
		
		// Looping
		while (true) {
			
			// Waiting and receiving the request datagram
			socket.receive (requestDatagram);
			
			// Getting the request source -- the client socket address
			SocketAddress clientSocketAddress = requestDatagram.getSocketAddress ();
			
			// Getting the request buffer positions
			int requestOffset = requestDatagram.getOffset ();
			int requestLength = requestDatagram.getLength ();
			
			// Creating the request string
			String requestString = new String (requestBuffer, requestOffset, requestLength);
			
			// Creating a response string based on the request string
			String responseString;
			if (requestString.equals ("hello"))
				responseString = "hello back";
			else if (requestString.equals ("get-time"))
				responseString = Long.toString (System.currentTimeMillis ());
			else if (requestString.equals ("get-random"))
				responseString = Long.toString ((long) Math.rint (Math.random () * 1000));
			else if (requestString.equals ("quit"))
				break;
			else
				responseString = "please?";
			
			// Creating the response buffer
			byte[] responseBuffer = responseString.getBytes ();
			int responseOffset = 0;
			int responseLength = responseString.length ();
			
			// Creating the response datagram
			DatagramPacket responseDatagram = new DatagramPacket (
					responseBuffer, responseOffset, responseLength, clientSocketAddress);
			
			// Sending the response datagram
			socket.send (responseDatagram);
		}
	}
}

Assignment

edit

This is the second assignment, so please commit it to the folder assignment-02.

Write a UDP client that:

  • take two arguments, an IP address and a port number
    • The first argument represents an broadcast address
    • The second arguments represents the port number used by the server
  • The client detects the server IP address using the broadcast address supplied as first argument:
    • The client sends a datagram containing "PING" to the broadcast address (eg. 10.10.10.255), port number 7654
    • The server receives the datagram and responds with a datagram containing "PONG" directly to the client (unicast), port number is computed directly from the previous datagram
    • The client responds with a datagram containing "PING-PONG" directly to the server which repplyed (unicast);
    • If the client receives any other message than "PONG" it should continue to wait for the right message;

Important: When sending the PING-PONG message, be careful to send it on the socket address from where the datagram came. See the method getSocketAddress from DatagramPacket class.

Example dialog

edit
client->broadcast PING
server->client PONG
client->server PING-PONG

Server Source

edit

This code can be foud under the folder examples/example-04 inside the SVN repository.

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;


public class Server {
	public static void main(String[] args) throws Exception {
		// Creating and binding the server socket
		DatagramSocket socket = new DatagramSocket(new InetSocketAddress(
				InetAddress.getByName("0.0.0.0"), 7654));
		// Creating the request buffer
		byte[] requestBuffer = new byte[2048];
		// Creating the request datagram
		DatagramPacket requestDatagram = new DatagramPacket(requestBuffer, 0,
				requestBuffer.length);
		String responseString;
		byte[] responseBuffer;
		int responseOffset = 0;
		int reponseLength;
		while (true) {
			socket.receive(requestDatagram);
			InetAddress clientAddr = requestDatagram.getAddress();
			requestBuffer = requestDatagram.getData();
			String request = new String(requestBuffer, 0, requestDatagram
					.getLength());

			if (request.equals("PING")) { // Send Pong back
				System.out.println("PING <- " + clientAddr.getHostAddress());
				responseString = "PONG";
				responseBuffer = responseString.getBytes();
				reponseLength = responseString.length();


				DatagramPacket responseDatagram = new DatagramPacket(
						responseBuffer, responseOffset, reponseLength);
				responseDatagram.setAddress(clientAddr);
				responseDatagram.setPort(requestDatagram.getPort());

				socket.send(responseDatagram);
				System.out.println("PONG -> " + clientAddr.getHostAddress());

			} else if (request.startsWith("PING-PONG")) {
				System.out.println("ESTABLISHED <-> "
						+ clientAddr.getHostAddress());
			}
		}
	}

}