10.3 El Pinger

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.

10.3.1 El servidor

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:

  1. 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.

  2. 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

____________________________________________________________

10.3.2 El cliente

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

____________________________________________________________