Programacion en red

Un objeto java.net.DatagramSocket es un "conector" a trav�s del cual enviamos y recibimos paquetes UDP. En la literatura t�cnica estos paquetes se denominan Datagramas.

La forma usual de crear un DatagramSocket para recibir paquetes es especificando un n�mero de puerto en el constructor. De esta forma, este DatagramSocket estar� "escuchando" en el puerto especificado, preparado para recibir cualquier paquete entrante.

Si queremos construir un DatagramSocket para enviar paquetes, no es necesario especificar el n�mero de puerto, ya que nos es indiferente. No ocurre lo mismo en el caso anterior, ya que siempre querremos usar un puerto fijo conocido por el resto de aplicaciones.

DatagramSocket ds1 = new DatagramSocket(123);  
/* Aqu� usamos este DatagramSocket para recibir datos... */  
/* ...              */  
/* Hemos terminado, cerramos el socket      */  
ds1.close();  
   
DatagramSocket ds2 = new Datagramsocket();  
/* Aqu� lo usamos para transmitir datos...  */  
/* ...            */  
/* Hemos terminado, cerramos el socket      */  
ds2.close();

.�La clase DatagramPacket

Esta clase representa a los paquetes de datos que vamos a recibir o transmitir a trav�s de los objetos DatagramSocket. Estos paquetes constan de una cabecera (que incluye la direcci�n de origen y destino del paquete, el puerto, la longitud del paquete, un checksum, etc.) y un cuerpo (donde se encuentra el contenido real del paquete).

En Java accedemos a las distintas partes de un datagrama mediante los m�todos de la clase java.net.DatagramPacket.

La forma de construir datagramas es distinta dependiendo de si queremos enviar o recibir datos. En caso de que �nicamente queramos recibir, debemos especificar un array de bytes donde almacenar los datos y un n�mero entero con la longitud m�xima que queremos recibir.

Si queremos transmitir, debemos especificar el buffer de datos que queremos enviar, la longitud m�xima de datos, la direcci�n y el puerto de destino del datagrama. La direcci�n de destino se especifica mediante un objeto de tipo InetAddress, mientras que el puerto se indica mediante un n�mero entero.

Veamos un ejemplo, donde enviamos un datagrama a una determinada direcci�n, suponiendo que tenemos un objeto InetAddress correctamente creado. N�tese el uso del m�todo send() de la clase DatagramSocket para enviar el datagrama:

int tam = 1024;  
 InetAddress direcc = ...;  
 byte[] datos = new byte[tam];  
 int puerto = 543;  
 for (int n=0;n<tam;n++){  
         /* Generamos los datos que vamos a enviar */  
         datos[n] = ...;  
         }  
   
 DatagramSocket ds = new DatagramSocket();  
 DatagramPacket dp = new DatagramPacket(datos, tam, direcc, puerto);  

 ds.send(dp);            /* Aqu� enviamos el paquete     */

Como se ve, la clase DatagramPacket solo nos da herramientas para enviar matrices de bytes a trav�s de UDP. Si queremos transmitir datos m�s complejos (cadenas de texto, enteros largos, n�meros en coma flotante, objetos Java, etc.) debemos ser nosotros los encargados de codificar esa informaci�n dentro de un array de bytes.

Si lo que queremos es recibir datos, solo necesitamos reservar espacio para la informaci�n entrante (un array de bytes), poner un objeto DatagramSocket escuchando en un puerto y esperar a recibir un paquete mediante el m�todo receive().

int tam = 1024;  
 byte[] buffer = new byte[tam];  

 int puerto = 987;  
 DatagramSocket ds = new Datagramsocket(puerto);  
 DatagramPacket dp = new DatagramPacket(buffer,tam);  
   
 ds.receive(dp);  
 // Ahora tenemos en buffer la informaci�n que nos interesa

.�La clase InetAddress

�C�mo se usa la clase java.net.InetAddress que aparec�a en uno de los ejemplos anteriores?.

La forma de crear un objeto InetAddress es mediante el m�todo est�tico InetAddress.getByName(String), que recibe un nombre de host en notaci�n alfanum�rica (por ejemplo "www.etsit.upv.es" o "209.41.57.70" y devuelve un objeto InetAddress con esa direcci�n. Si la direcci�n no existe o no puede ser encontrada, este m�todo lanza una UnknownHostException.

Por ejemplo, si queremos mandar una matriz de bytes al puerto 90 de la direcci�n "www.upv.es", tenemos que escribir:

int tam = ...;  
 int puerto = 90;  
 String maquina = "www.upv.es";  
 byte[] buffer = new byte[tam];  
 // ...  
 // Generamos el contenido del buffer  
 // ...  
 InetAddress direcc = InetAddress.getByName(maquina);  
 DatagramSocket ds = new DatagramSocket();  
 DatagramPacket dp = new DatagramPacket(buffer, tam, direcc, puerto);  

 ds.send(dp);            /* Aqu� enviamos el paquete     */

Si queremos enviar paquetes a nuestra propia m�quina hay que usar como nombre de host la direcci�n "localhost" o "127.0.0.1". Tambi�n podemos usar el m�todo InetAddress.getLocalHost(), que devuelve un objeto InetAddress que "apunta" a la m�quina local.

.�Un ejemplo cliente/servidor completo usando UDP

A continuaci�n se incluye una aplicaci�n completa, formada por un cliente y un servidor que se comunican mediante UDP. El cliente env�a n�meros enteros (32 bits) al servidor y este se los devuelve despu�s de procesarlos (simplemente los eleva al cuadrado), en forma de enteros largos (64 bits).

En ambos programas (cliente y servidor) se utilizan varias clases explicadas con anterioridad (es fundamental haber le�do previamente el cap�tulo de Entrada/Salida) y se realiza un extensivo control de excepciones.

Los datos que el cliente env�a al servidor los obtiene de la l�nea de comandos.

Veamos un ejemplo en el que el cliente manda los n�meros 43, 56 y 2 al servidor. Si ejecutamos el servidor en una m�quina con direcci�n IP 194.140.47.1 y ejecutamos el cliente en otra m�quina con direcci�n IP distinta, tendr�amos que teclear la siguiente en la l�nea de comandos:

Servidor:

java servidor

Cliente:

java cliente 194.140.47.1 43 56 2

Como cada paquete enviado es independiente de los dem�s, podr�amos tener varios clientes comunic�ndose con el servidor al mismo tiempo, con lo que los distintos paquetes se intercalar�an unos con otros.

Si ejecutamos ambos programas en la misma m�quina tendr�amos que abrir dos sesiones distintas y teclear las instrucciones anteriores en cada sesi�n. En el caso del cliente tendr�amos que poner:

java cliente localhost 43 56 2

Veamos el c�digo del programa:

import java.net.*;  
      import java.io.*;  

      class servidor{  

         public static void main(String args[]){  
           
   // Primero indicamos la direcci�n IP local  
   try{  
  System.out.println("LocalHost = " + InetAddress.getLocalHost().toString());  
  } catch (UnknownHostException uhe){  
     System.err.println("No puedo saber la direcci�n IP local : " + uhe);  
     }  
 
 
    // Abrimos un Socket UDP en el puerto 1234.  
    // A trav�s de este Socket enviaremos datagramas del tipo DatagramPacket  
    DatagramSocket ds = null;  
    try{  
  ds = new DatagramSocket(1234);  
  } catch(SocketException se){  
     System.err.println("Se ha producido un error al abrir el socket : " + se);  
     System.exit(-1);  
     }  
 
            // Bucle infinito  
            while(true){  
  try{  
  // Nos preparamos a recibir un n�mero entero (32 bits = 4 bytes)           
  byte bufferEntrada[] = new byte[4];  
    
  // Creamos un "contenedor" de datagrama, cuyo buffer  
  // ser� el array creado antes  
  DatagramPacket dp = new DatagramPacket(bufferEntrada,4);  
    
  // Esperamos a recibir un paquete  
  ds.receive(dp);  
    
  // Podemos extraer informaci�n del paquete  
  // N� de puerto desde donde se envi�  
  int puerto = dp.getPort();  
  // Direcci�n de Internet desde donde se envi�  
  InetAddress direcc = dp.getAddress();  
    
    
  // "Envolvemos" el buffer con un ByteArrayInputStream...  
  ByteArrayInputStream bais = new ByteArrayInputStream(bufferEntrada);  
  // ... que volvemos a "envolver" con un DataInputStream  
  DataInputStream dis = new DataInputStream(bais);  
  // Y leemos un n�mero entero a partir del array de bytes  
  int entrada = dis.readInt();  
    
  long salida = (long)entrada*(long)entrada;  
             
 // Creamos un ByteArrayOutputStream sobre el que podamos escribir  
  ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  // Lo envolvemos con un DataOutputStream  
  DataOutputStream dos = new DataOutputStream(baos);  
  // Escribimos el resultado, que debe ocupar 8 bytes  
  dos.writeLong(salida);  
  // Cerramos el buffer de escritura  
  dos.close();  
    
  // Generamos el paquete de vuelta, usando los datos  
  // del remitente del paquete original  
  dp = new DatagramPacket(baos.toByteArray(),8,direcc,puerto);  
    
  // Enviamos  
  ds.send(dp);  
     
  // Registramos en salida estandard   
  System.out.println(  "Cliente = " + direcc + ":" + puerto +  
          "\tEntrada = " + entrada +  
          "\tSalida = " + salida );  
  } catch(Exception e){  
     System.err.println("Se ha producido el error " + e);  
     }  
  }  
    
            }  
 
        }  
    
    

      class cliente {  

         public static void main(String args[]){  
 
            // Leemos el primer par�metro, donde debe ir la direcci�n  
            // IP del servidor  
            InetAddress direcc = null;  
            try{  
  direcc = InetAddress.getByName(args[0]);  
  } catch(UnknownHostException uhe){  
     System.err.println("Host no encontrado : " + uhe);  
     System.exit(-1);  
     }  
 
            // Puerto que hemos usado para el servidor  
            int puerto = 1234;  
 
            // Creamos el Socket  
            DatagramSocket ds = null;  
            try{  
  ds = new DatagramSocket();  
  } catch(SocketException se){  
     System.err.println("Error al abrir el socket : " + se);  
     System.exit(-1);  
     }  
 
   // Para cada uno de los argumentos...  
   for (int n=1;n<args.length;n++){  
  try{  
  // Creamos un buffer para escribir  
  ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  DataOutputStream dos = new DataOutputStream(baos);  
  // Convertimos el texto en n�mero  
  int numero = Integer.parseInt(args[n]);  
  // Lo escribimos  
  dos.writeInt(numero);  
  // y cerramos el buffer  
  dos.close();  
    
  // Creamos paquete  
  DatagramPacket dp = new DatagramPacket(baos.toByteArray(),4,direcc,puerto);  
  // y lo mandamos  
  ds.send(dp);  
    
    
  // Preparamos buffer para recibir n�mero de 8 bytes  
  byte bufferEntrada[] = new byte[8];  
  // Creamos el contenedor del paquete  
  dp = new DatagramPacket(bufferEntrada,8);  
  // y lo recibimos  
  ds.receive(dp);  
    
    
  // Creamos un stream de lectura a partir del buffer  
  ByteArrayInputStream bais = new ByteArrayInputStream(bufferEntrada);  
  DataInputStream dis = new DataInputStream(bais);  
    
  // Leemos el resultado final  
  long resultado = dis.readLong();  
    
  // Indicamos en pantalla  
  System.out.println(  "Solicitud = " + numero +  
          "\tResultado = " +resultado );  
  } catch (Exception e){  
     System.err.println("Se ha producido un error : " + e);  
     }  
  }  
    
            }  
         }

En el c�digo anterior se usan m�todos no vistos hasta ahora, como DatagramPacket.getAddress(), que devuelve la direcci�n IP del remitente del mensaje, y DatagramPacket.getPort(), que devuelve el puerto. Existen otros m�todos importantes, como DatagramPacket.getLength(), que indican la longitud de datos recibidos.

.�Consideraciones sobre UDP

UDP es un protocolo no orientado a la conexi�n. Esto significa que la comunicaci�n es m�s r�pida (porque no hay que establecer conexiones ni circuitos virtuales entre m�quinas) pero tambi�n menos segura.

Nadie nos garantiza que un paquete vaya a llegar a su destino. Tampoco est� garantizado que dos paquetes lleguen en el mismo orden en que se enviaron. Si nuestros programas usan una red local privada para comunicarse, es muy improbable que estos problemas aparezcan. Pero si deben usar una red p�blica como Internet, con m�ltiples pasarelas y enlaces distintos entre los punto de origen y destino, cualquiera de estos errores puede ocurrir (y ocurrir�, seguro).

En estos casos es responsabilidad del programador el comprobar que los paquetes llegan a sus destino y que la informaci�n se transmite en orden. Existen multitud de mecanismos para conseguir esto, pero la complejidad de su implementaci�n hace m�s recomendable la utilizaci�n de un protocolo orientado a la conexi�n como TCP.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO