El Pinger presentado es una aplicación que utiliza el UDP. El cliente envía paquetes UDP
y el servidor los recibe y los retorna. Para describir ambas partes se ha utilizado el
lenguaje de programación Java.
El servidor es básicamente un bucle infinito que espera a recibir paquetes UDP. Para
ello escucha en el puerto que se le indica por la línea de comandos cuando es
ejecutado.
Como la mayoría de las ocasiones tendremos que ejecutar el cliente y el servidor en el
mismo host, el servidor puede configurarse para:
- Simular la pérdida de paquetes: Cuando el servidor y el cliente están
suficientemente alejados (en hops) los paquetes deberían perderse con una
cierta probabilidad (puesto que el UDP no es un protocolo fiable (como el
TCP), no todos los paquetes que el cliente envía van a llegar al servidor y no
todos los paquetes que el servidor devuelve al cliente van a llegar a éste).
Sin embargo, dentro del mismo host o incluso en la red de laboratorio,
es prácticamente imposible que se pierdan paquetes. Para simular dicho
comportamiento el servidor deja de enviar un eco al cliente con una cierta
probabilidad controlada por el parámetro que llamado LOSS_RATE.
- Simular el retraso promedio de los paquetes: El parámetro
AVERAGE_DELAY se encarga de retrasar las contestaciones del servidor una
determinada cantidad de tiempo, en promedio. De esta forma, se simula
además una cierta distancia entre el servidor y el cliente, aunque estos estén
ejecutándose en el mismo host.
A continuación se presenta el código que implementa el servidor (
http://www.ace.ual.es/\~{}vruiz/docencia/redes/practicas/progs/PingServer.java):
import java.io.*;
import java.net.*;
import java.util.*;
/*
* Un servidor que procesa peticiones ping sobre UDP.
*/
public class PingServer {
private static final double LOSS_RATE = 0.3;
private static final int AVERAGE_DELAY = 100; // milisegundos
public static void main(String[] args) throws Exception {
/* Comprobamos que se han introducido los parámetros de entrada
desde la línea de comandos. */
if (args.length != 1) {
System.out.println("You must specify a port");
return;
}
/* Puerto de escucha del servidor. */
int port = Integer.parseInt(args[0]);
/* Usaremos un generador de números aleatorios para simular la
* pérdida de paquetes y el retraso de la red. */
Random random = new Random();
/* Creamos un socket de tipo DatragramSocket para recibir y
* enviar paquetes UDP. */
DatagramSocket socket = new DatagramSocket(port);
/* Processing loop. */
while (true) {
/* Creamos la estructura de datos que nos servirá para
almacenar un paquete UDP. */
DatagramPacket request = new DatagramPacket(new byte[1024], 1024);
/* Nos bloqueamos hasta que se recibe el siguiente paquete
UDP. */
socket.receive(request);
/* Imprimimos el payload del paquete. */
printData(request);
/* Decidimos si responder o simular la pérdida del paquete. */
if (random.nextDouble() < LOSS_RATE) {
System.out.println(" Reply not sent.");
continue;
}
/* Simulamos el retraso de la red. */
Thread.sleep((int) (random.nextDouble() * 2 * AVERAGE_DELAY));
/* Enviamos la respuesta. */
InetAddress clientHost = request.getAddress();
int clientPort = request.getPort();
byte[] buf = request.getData();
DatagramPacket reply = new DatagramPacket(buf, buf.length, clientHost, clientPort);
socket.send(reply);
System.out.println(" Reply sent.");
}
}
/*
* Imprime los datos del datagrama (payload) sobre la salida estándar.
*/
private static void printData(DatagramPacket request) throws Exception {
/* "buf" apunta al comienzo de los datos en el paquete. */
byte[] buf = request.getData();
/* Convertimos "buf" en un stream de entrada de bytes. */
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
/* Convertimos "bais" en un stream de entrada de caracteres. */
InputStreamReader isr = new InputStreamReader(bais);
/* Convertimos "isr" en un stream de entrada de caracteres
buffereado. Así podremos leer líneas completas. Una línea
es cualquier combinación de caracteres que acaba en
cualquier combinación de \r y \n. */
BufferedReader br = new BufferedReader(isr);
/* Los datos del mensaje están contenidos en una única
// línea. Así la leemos. */
String line = br.readLine();
/* Imprimimos la dirección del host que envía el mensaje y los
// datos del mismo. */
System.out.println("Received from " +
request.getAddress().getHostAddress() +
": " +
new String(line) );
}
}
__________________________________________________________________
Taller 10.1: Compile el servidor usando el comando:
# Este comando genera el fichero "PingServer.class".
javac PingServer.java
Evidéntemente, deberá tener instalado un compilador de Java para realizar
la anterior acción. Existen versiones tanto para Linux como para
Windows. Se recomienda el compilador y la máquina virtual de Sun
para realizar esta parte de la práctica ( http://java.sun.com).
_____________________________________________________________
Taller 10.2: Ejecute el servidor con el comando:
# Recuerde que hay que utilizar un puerto mayor que 1024
# si no somos el usuario root. Además, el puerto no debería estar
# ocupado por ningún otro proceso.
# El fichero "PingServer.class", generado tras la compilación,
# debería existir en el directorio actual.
java PingServer 6789
____________________________________________________________
El cliente envía 10 peticiones de ping al servidor especificado. Cada mensaje contiene un
payload de datos donde figura la cadena PING, un número de secuencia y una estámpa
de tiempo.
Tras enviar un paquete, el cliente espera hasta un segundo para recibir una respuesta.
Si transcurrido este tiempo ésta no llega, el cliente supone que su paquete de petición o
el paquete de respuesta (o ambos) se ha(n) perdido.
A continuación se muestra el código del cliente (
http://www.ace.ual.es/\~{}vruiz/docencia/redes/practicas/progs/PingClient.java):
import java.io.*;
import java.net.*;
import java.util.*;
/*
* Un cliente que genera peticiones ping sobre UDP.
*/
public class PingClient {
public static final String CRLF="\r\n";
public static void main(String[] args) throws Exception {
/* Comprobamos que se han introducido los parámetros de entrada
desde la línea de comandos. */
if (args.length != 2) {
System.out.println("You must specify a server and a port");
return;
}
/* Obtenemos la dir IP del servidor a partir de su nombre. */
InetAddress serverAddress = InetAddress.getByName(args[0]);
/* Obtenemos el puerto en el que escucha el servidor. */
int serverPort = Integer.parseInt(args[1]);
/* Creamos un socket de tipo DatragramSocket para recibir y
* enviar paquetes UDP. */
DatagramSocket socket = new DatagramSocket();
/* Asignamos un tiempo máximo de espera de recepción a través
* de este socket (time-out) de un segundo. */
socket.setSoTimeout(1000);
/* Creamos la estructura de datos que almacenará un paquete UDP. */
DatagramPacket reply = new DatagramPacket(new byte[1024], 1024);
/* Enviamos los 10 paquetes. */
for (int i=0; i<10; ++i) {
/* Generamos el payload del paquete con el instante en que
* el mismo es generado. */
long sendingDate = (new Date()).getTime();
String payload = "Ping " + (i+1) + " " + sendingDate + CRLF;
DatagramPacket request =
new DatagramPacket(payload.getBytes(),
payload.getBytes().length,
serverAddress,
serverPort);
System.out.print("Packet sent with payload: " + payload);
/* Enviámos el paquete. */
socket.send(request);
/* Esperamos la respuesta del servidor. */
try {
socket.receive(reply);
}
catch(SocketTimeoutException e) {
/* Se ha producido un time-out. */
System.out.println("Not response from server");
continue;
}
/* Calculamos el tiempo que el paquete ha tardado en regresar. */
long receivingDate = (new Date()).getTime();
System.out.println("Packet received after " +
(receivingDate-sendingDate) + " miliseconds");
}
}
}
__________________________________________________________________
Taller 10.3: Compile y ejecute el cliente:
javac PingClient.java
java PingClient localhost 6789
____________________________________________________________
- Presente un ejemplo de interacción entre el cliente y el servidor.
- Escriba una versión del cliente y del servidor Pinger usando el lenguaje
de programación C. Compruebe que sus versiones funcionan y presente un
ejemplo de interacción entre el cliente y el servidor, escritos ambos en C.
Presente también el código desarrollado debidamente comentado.
- Compruebe que puede intercambiar cada una de sus aplicaciones por la
respectiva aplicación escrita en Java. Muéstrese un ejemplo de interacción.
- Realice un experimento que le permita comprobar si el servidor puede atender
a más de un cliente a la vez, es decir, si podemos ejecutar más de un cliente
simultáneos que usen el mismo servidor. Muestre dicho experimento.
- Instale la máquina virtual de Java en el sistema operativo anfitrión y
compruebe si el servidor puede servir a un cliente ejecutado en el PC virtual.
Realice lo mismo, pero ahora ejecute el cliente en el host anfitrión y el servidor
en el PC virtual. Muestre dichos experimentos.
- Ejecute un servidor en un PC virtual dentro de un host X y un cliente en otro
PC virtual dentro de un host Y. ¿Son capaces de comunicarse? Explique su
respuesta.
- Ejecute un servidor en un host X (no virtual) y un cliente en host Y (no
virtual). ¿Son capaces de comunicarse? Explique su respuesta.
- Cree una copia del PC virtual (el PC “origen” debería estar apagado) y ejecute
ambas instancias en un mismo host. Ejecute un servidor en uno y un cliente
en otro. ¿Son capaces de comunicarse? Explique su respuesta.