Programación en castellano
Inicio > Tutoriales > J2SE > Programacion en red
-Tutoriales

Programacion en red


Trabajo en red. Parte 3

. Comunicación mediante el protocolo TCP

. La clase Socket

Un objeto java.net.Socket es un "conector" a través del cual enviamos y recibimos datos mediante el protocolo TCP. A diferencia de los "conectores" java.net.DatagramSocket, que eran usados para enviar paquetes sueltos, estos "conectores" TCP sirven para enviar o recibir datos de forma continua, como si trabajáramos con un flujo InputStream o OutputStream.

El hecho de que el protocolo subyacente sea TCP nos permite olvidarnos de detalles relacionados con la pérdida de datos, ya que es el propio protocolo el encargado de hacerlo por nosotros. A todos los efectos, podemos tratar un Socket TCP como un canal carente de errores.

¿Cómo se usa un objeto Socket? La inicialización de estos objetos es más compleja que en el caso de DatagramSocket, ya que es necesario que previamente haya alguien "escuchando" en el extremo receptor.

Suponiendo que, de alguna forma, hay un programa "escuchando" en el puerto 1234 de la máquina con dirección IP 209.41.57.70, la inicialización de nuestro Socket sería:

   InetAddress d = InetAddress.getByName("209.41.57.70"); 
   Socket s = new Socket(d,1234); 
   /* Utilizacion del socket */ 
   ... 
   /* Cerramos el socket */ 
   s.close();

Una vez tenemos un Socket abierto con otra máquina, podemos obtener un flujo de entrada o de salida para poder recibir o transmitir datos. Esto se hace con los métodos Socket.getInputStream() y Socket.getOutputStream():

Veamos un ejemplo donde abrimos un socket, leemos los bytes que nos transmitan desde el otro extremo y los imprimimos en pantalla:

   InetAddress d = InetAddress.getByName("209.41.57.70"); 
   Socket s = new Socket(d, 1234); 
   Inputstream is = s.getInputStream(); 
   while((int dato=is.read())!=-1){ 
   System.out.println("Recibido " + dato); 
   } 
   is.close(); 
   s.close();

. La clase ServerSocket

La clase java.net.ServerSocket es el mecanismo mediante el cual nuestros programas pueden quedarse "escuchando" en un puerto, esperando conexiones entrantes. La forma general de trabajar con sockets será entonces: Un programa (lo llamaremos "servidor") crea un ServerSocket en un determinado puerto conocido por el resto de programas. El servidor queda esperando a que algún cliente intente conectar con él. En el momento en que se establece la conexión, ambos programas (el cliente y el servidor) obtienen un objeto Socket. Mediante objetos InputStream y OutputStream obtenidos a través de los objetos Socket, el cliente y el servidor intercambian datos. Uno de los dos programas cierra la conexión.

La parte del cliente ya la hemos visto en los apartados anteriores. Veamos como se realiza la parte del servidor.

La forma más sencilla de crear un objeto ServerSocket es indicando el número de puerto al constructor:

   ServerSocket ss = new ServerSocket(1234); 
   /* Utilizamos el objeto ServerSocket */ 

Una vez creado, tenemos que quedarnos esperando a que alguien intente realizar la conexión. Esto se consigue mediante la función ServerSocket.accept(). Esta función espera una conexión entrante, y devuelve un objeto de tipo Socket.

   ServerSocket ss = new ServerSocket(1234); 
   Socket s = ss.accept(); 
   /* Utilizamos el objeto Socket */ 
   s.close();

Una vez el servidor tiene el objeto Socket, puede realizar las mismas acciones que el cliente (extraer los flujos de entrada/salida, cerrar la conexión, etc.)

En el siguiente ejemplo, nuestro servidor espera la conexión entrante y responde con un mensaje de bienvenida:

   ServerSocket ss = new ServerSocket(1234); 
   Socket s = ss.accept(); 
   
   OutputStream os = s.getOutputStream(); 
   String mensaje = "¡Conéctate con otro sitio y déjame en paz, pesado!"; 
   byte[] matriz = mensaje.getBytes(); 

   os.write(matriz); 
   os.close(); 
   s.close();

De un objeto ServerSocket se pueden obtener muchos objetos Socket diferentes, cada uno independiente de los demás. Por ejemplo, podemos tener un programa que trabaje en el puerto 80 y que asigne cada nueva conexión a un hilo de ejecución distinto. No es necesario que se cierren los objetos Socket previos antes de poder aceptar una nueva conexión. Esto es lo que hace que los servidores Web puedan atender a varias personas al mismo tiempo, sin tener que esperar a terminar con cada cliente antes de atender al siguiente.

Por ejemplo, supongamos que tenemos una clase de objetos "MiniServidor", que implementan la interfaz Runnable y que están programados para responder a las peticiones que les llegan a través de un Socket. Una posible implementación para el servidor sería:

   Serversocket ss = new ServerSocket(1234); 
   while(true){ 
   Socket s = ss.accept(); 
   MiniServidor m = new MiniServidor(s); 
   Thread t = new Thread(m); 
   t.start(); 
   }

. Gestión de excepciones

Como en todos los casos en que tengamos que trabajar con protocolos de red o sistemas de entrada/salida, tenemos que encargarnos de gestionar las posibles excepciones. Las más comunes son java.io.IOException (para los casos en los que haya problemas con la conexión) y java.net.UnknownHostException (cuando especificamos una dirección IP desconocida o incorrecta).

. Un ejemplo cliente/servidor completo usando TCP

Con el fin de comparar ambos métodos, vamos a realizar un par de programas que realicen la misma función que el ejemplo anterior, pero usando el protocolo TCP en vez de UDP.

Veremos una implementación en la que para cada nuevo número transmitido el cliente abre una conexión TCP distinta.Aunque este sistema es perfectamente válido, no es muy eficiente, ya que pierde mucho tiempo en el proceso de establecimiento de las conexiones. Sería más deseable una implementación en la que todas las peticiones fueran a través del mismo socket TCP.

El funcionamiento del programa es muy similar al del que usaba Datagramas. Se realiza un completo control de errores.

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

  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 de Servidor" TCP en el puerto 1234. 
 ServerSocket ss = null; 
 try{ 
   ss = new ServerSocket(1234); 
   } catch (IOException ioe){ 
   System.err.println("Error al abrir el socket de servidor : " + ioe); 
   System.exit(-1); 
   } 

  
 int entrada; 
 long salida; 
 // Bucle infinito 
 while(true){ 
   try{ 
   // Esperamos a que alguien se conecte a nuestro Socket 
 
   Socket sckt = ss.accept(); 
 
   // Extraemos los Streams de entrada y de salida 
   DataInputStream dis = new DataInputStream(sckt.getInputStream()); 
 
   DataOutputStream dos = new DataOutputStream(sckt.getOutputStream()); 
 
   // Podemos extraer información del socket 
   // Nº de puerto remoto 
   int puerto = sckt.getPort(); 
 
   // Dirección de Internet remota 
   InetAddress direcc = sckt.getInetAddress(); 
   
   // Leemos datos de la peticion 
 
   entrada = dis.readInt(); 
   // Calculamos resultado 
 
   salida = (long)entrada*(long)entrada; 

   // Escribimos el resultado 
 
   dos.writeLong(salida); 
   // Cerramos los streams 
   dis.close(); 
   dos.close(); 
 
   sckt.close(); 
 
 
   // Registramos en salida estandard  
   System.out.println(  "Cliente = " + direcc + ":" + puerto + 
  "\tEntrada = " + entrada + 
  "\tSalida = " + salida ); 
   } catch(Exception e){ 
   System.err.println("Se ha producido la excepción : " +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; 
 
 // Para cada uno de los argumentos... 
 for (int n=1;n<args.length;n++){ 
   Socket sckt = null; 
   DataInputStream dis = null; 
   DataOutputStream dos = null; 
   try{ 
 
   // Convertimos el texto en número 
   int numero = Integer.parseInt(args[n]); 
 
   // Creamos el Socket 
 
   sckt = new Socket(direcc,puerto); 
 
   // Extraemos los streams de entrada y salida 
   dis = new DataInputStream(sckt.getInputStream()); 
 
   dos = new DataOutputStream(sckt.getOutputStream());   
 
 
   // Lo escribimos 
   dos.writeInt(numero); 
    
   // Leemos el resultado final 
 
   long resultado = dis.readLong(); 
 
   // Indicamos en pantalla 
   System.out.println(  "Solicitud = " + numero + 
  "\tResultado = " +resultado ); 
   // y cerramos los streams y el socket 
   dis.close(); 
   dos.close(); 
   } catch(Exception e){ 
   System.err.println("Se ha producido la excepción : " +e); 
   } 
    
   try{ 
   if (sckt!=null) sckt.close(); 
   } catch(IOException ioe){ 
  System.err.println("Error al cerrar el socket : " + ioe); 
  } 
 
   } 
 
 } 
  }

Aparte de la introducción de los métodos Socket.getPort() y Socket.getInetAddress(), que nos devuelven, respectivamente, el puerto y la dirección IP de la máquina remota, el código anterior únicamente resume las ideas presentadas con anterioridad.

Se deja como "ejercicio para el lector" el cambiar el código anterior para que todas las peticiones que el cliente transmite al servidor vayan por el mismo socket TCP.

. Trabajo con URLs

Una URL (Uniform Resource Locator) es, a grandes rasgos, el nombre de un determinado recurso (archivos, bases de datos, ordenadores, impresoras, etc) en Internet. Por ejemplo, http://www.akal.com/index2.htm es una URL que "apunta" a una página dentro de un servidor Web.

El formato de una URL está definido en el standard RFC 1738, y suele seguir el esquema:

PROTOCOLO://MAQUINA/DIRECTORIO/SUBDIRECTORIO/ARCHIVO

Por ejemplo:

   ftp://ftp.microsoft.com/public/file.txt 
   http://www.etsit.upv.es/iaeste/index.html

Una forma más general, que nos permite especificar el puerto de conexión, así como el login y el password es la siguiente:

http://login:password@maquina:puerto/dir/subdir/archivo

En Java, las URLs se representan mediante la clase java.net.URL. Esta clase solo representa la dirección, no su contenido. Para acceder al contenido de un objeto URL necesitamos obtener un objeto java.net.URLConnection, extraído a partir del propio URL.

Por ejemplo, el siguiente código crea una URL que apunta a http://www.javasoft.com, posteriormente obtiene un objeto URLConnection y por último hace algo realmente útil: leer el contenido de la URL:

   URL direccion = new URL("http://www.javasoft.com"); 
   URLConnection conex = direccion.openConnection(); 
   InputStream entrada = conex.getInputStream(); 
   while((int dato=entrada.read())!=-1){ 
   /* Hacemos algo interesante con lo que leemos */ 
   }

El ejemplo anterior es una muestra muy simple de como utilizar las clases URL y URLConnection. En una aplicación real probablemente habría que usar el resto de métodos que nos proporciona la clase URLConnection (para conocer datos del recurso remoto, leer información sobre la comunicación y el tipo de protocolo usado, etc.)

Veamos un ejemplo completo. El siguiente programa simplemente se conecta con una URL especificada en la línea de comandos y guarda el contenido en un archivo.

  /* Este programa muestra como trabajar con URL's a través de Java */ 

  /* El programa se limita a acceder a una URL, extraer su stream de salida, 
  y leer los datos byte a byte, guardándolos en el archivo especificado */ 

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

  class getURL{ 

  public static void main(String params[]){ 
  
 /* El programa debe recibir dos parámetros:  
   1.- la URL 
   2.- el archivo local donde guardar el resultado */ 
 if (params.length<2){ 
   System.err.println("Necesito una URL y un nombre de archivo local válidos"); 
   System.exit(-1); 
   } 
 /* Intentamos acceder a la URL */ 
  
 try{ 
   /* Si la URL fuera incorrecta saltaría al catch de MalformedURLException */ 
   URL miUrl = new URL(params[0]); 
   /* Obtenemos una URLConnection, mediante openConnection(), y sacamos 
   un InputStream mediante getInputStream() */ 
    
   /* Si se produce algún error salta al catch de IOException */ 
   InputStream is = miUrl.openConnection().getInputStream(); 
 
   /* Abrimos el archivo para escritura */ 
   FileOutputStream fos = new FileOutputStream(params[1]); 
 
   int dato; 
   /* Vamos leyendo bytes hasta que read() nos devuelva -1 */ 
   while ((dato=is.read()) != -1) 
   fos.write(dato); 
 
   /* Cerramos todos los streams */ 
   fos.close(); 
   is.close(); 
   } 
 catch(MalformedURLException errorURL){ 
   System.err.println("La URL " + params[0] + " es incorrecta"); 
   } 
 catch(IOException errorIO){ 
   System.err.println("Error de entrada/salida : " + errorIO); 
   } 
   }   /* fin main */ 
 }   /*fin clase */

La forma de invocar el programa sería como sigue:

java getURL http://www.akal.com/index2.htm mifichero.htm

En el código anterior además aparecen las excepciones típicas que aparecen en estos casos (java.net.MalformedURLException, java.io.IOException, etc.)

 
Patrocinados
 

Copyright © 1999-2007 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad

Hospedaje web y servidores dedicados linux por Ferca Network

red internet: musica mp3 | logos y melodias | hospedaje web linux | registro de dominios | servidores dedicados
más internet: comprar | recursos gratis | posicionamiento en buscadores | tienda virtual | gifs animados