Computer networks -- 2007-2008 -- info.uvt.ro/Laboratory 3
Quick links:
- front;
- courses 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13;
- laboratories agenda, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, evaluation, tools, repository.
Prerequisites
editThe prerequisites necessary for this laboratory are:
- all the prerequisites necessary for the second laboratory: Computer networks -- 2007-2008 -- info.uvt.ro/Laboratory 2#Prerequisites;
- ofcourse the entire second laboratory: Computer networks -- 2007-2008 -- info.uvt.ro/Laboratory 2, especially the Java IP generic API section;
- you should also need to know how to encode / decode binary data -- a helpful set of classes could be the ones implementing the DataInput and DataOutput interfaces;
References
edit- wikipedia:User Datagram Protocol
- wikibooks:Computer Networks/UDP
- How the Transport Layer Works
- please see also the references from the previous laboratory: Computer networks -- 2007-2008 -- info.uvt.ro/Laboratory 2#References;
Differences between TCP and UDP
editAs 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
editLike 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
editThus 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
editThe 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;
- datagram creation -- we create a datagram object for which we establish:
- 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;
- datagram creation -- we create a datagram object for which we establish:
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
editA 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;
- datagram creation:
- receiving a datagram -- identical;
- sending a datagram:
- destruction;
Java API
edit- java.net:
- DatagramSocket:
- constructor() -- creates a datagram socket and binds it to a random local port -- the IP address is 0.0.0.0;
- constructor(SocketAddress) -- creates a datagram socket and binds it to the specified socket address;
- connect(SocketAddress) -- this method is used to establish a default remote socket address for datagrams -- thus giving us the impression of a connection-oriented socket;
- send(DatagramPacket) -- sends a datagram;
- receive(DatagramPacket) -- waits and fills the datagram object; the buffer and remote socket address will be set-up;
- getLocalSocketAddress() -- used to obtain the local socket address;
- getRemoteSocketAddress() -- used to obtain the remote socket address -- the one setup by connect;
- close() -- used to destroy the socket, and after this it will not be usable;
- DatagramPacket:
- constructor(byte, int, int) -- the constructor used to establish only the data buffer and positions; it does not setup any remote socket address;
- constructor(byte, int, int, SocketAddress) -- like the previous constructor, but we are able to establish a remote socket address;
- getSocketAddress();
- getData();
- getLength();
- getOffset();
- DatagramSocket:
- please see also the API in the section Java binary IO generic API;
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
editThe 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
editWhen 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
editIn 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
editThe 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
editAs 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
editThis 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
editclient->broadcast PING server->client PONG client->server PING-PONG
Server Source
editThis 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());
}
}
}
}