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) );
    }
}
