viernes, 26 de octubre de 2012

Implementación en JAVA (Sockets)

1.1.1.        La clase URL
Para comenzar con la programación de sockets, resulta necesario comprender las clases que ofrece Java. En primer lugar, la clase URL contiene constructores y métodos para la manipulación de URL (Universal Resource Locator): un objeto o servicio  en  Internet.  El  protocolo  TCP necesita  dos  tipos de información: la dirección IP y el número de puerto. Vamos a ver como podemos recibir entonces la página Web siguiente:

http://www.yahoo.com

En primer lugar, Yahoo tiene registrado su nombre, permitiendo que se use yahoo.com como su dirección IP, o lo que es lo mismo, cuando indicamos yahoo.com es como si hubiésemos indicado 205.216.146.71, su dirección IP real.

Si queremos obtener la dirección IP real de la red en que estamos corriendo, podemos realizar llamadas a los métodos getLocalHost() y getAddress(). Primero, getLocalHost() nos devuelve un objeto iNetAddress, que si usamos con getAddress() generará un array con los cuatro bytes de la dirección IP, por ejemplo:

InetAddress direccion = InetAddress.getLocalHost();
byte direccionIp[] = direccion.getAddress();

Si la dirección de la máquina en que estamos corriendo es 150.150.112.145, entonces:

direccionIp[0] = 150 direccionIp[1] = 150 direccionIp[2] = 112 direccionIp[3] = 145

Por otro lado, podemos especificar las partes que componen una url y así podríamos construir un objeto de tipo url utilizando los siguientes constructores:

URL (“http","www.yahoo.com","80","index.html”);

o  dejar  que  los  sistemas  utilicen  todos  los  valores  por  defecto  que  tienen definidos, como en:

URL (“http://www.yahoo.com”);

Y en los dos casos obtendríamos la visualización de la página principal de Yahoo en nuestro navegador.

1.1.2.        Arquitectura de comunicaciones
En Java, crear una conexión socket TCP/IP se realiza directamente con el paquete java.net. A continuación mostramos un diagrama de lo que ocurre en el lado del cliente y del servidor:




El modelo de sockets más simple es:

·         El  servidor  establece  un  puerto  y  espera  durante  un  cierto  tiempo (timeout segundos),  a que el cliente establezca  la conexión. Cuando el cliente solicite una conexión, el servidor abrirá la conexión socket con el método accept().

·         El cliente establece una conexión con la máquina host a través del puerto que se designe en puerto#

·         El cliente y el servidor se comunican con manejadores InputStream y OutputStream

1.1.3.        La clase Socket
Si estamos programando un cliente, el socket se abre de la forma:

Socket miCliente; miCliente = new Socket (“maquina",numeroPuerto );

Donde maquina es el nombre de la máquina(host) en donde estamos intentando abrir la conexión y numeroPuerto es el puerto (un número) del servidor que está corriendo sobre el cual nos queremos conectar. Para las aplicaciones que se desarrollen, asegurarse de seleccionar un puerto por encima del 1023.

En el ejemplo anterior no se usan excepciones; sin embargo, es una gran idea la captura de excepciones cuando se está trabajando con sockets. El mismo ejemplo quedaría como:


Socket miCliente;

try {
miCliente = new Socket( "maquina",numeroPuerto );
} catch( IOException e ) {
System.out.println( e );
}

1.1.4.        La clase ServerSocket
Si estamos programando un servidor, la forma de apertura del socket es la que muestra el siguiente ejemplo:

Socket miServicio;

try {
miServicio = new ServerSocket( numeroPuerto );
} catch( IOException e ) {
System.out.println( e );
}

A la hora de la implementación de un servidor también necesitamos crear un objeto socket desde el ServerSocket para que esté atento a las conexiones que le puedan realizar clientes potenciales y poder aceptar esas conexiones:

Socket socketServicio = null;

try {
socketServicio = miServicio.accept();
} catch( IOException e ) {
 System.out.println( e );
}

1.1.5.        Creación de Streams de Entrada
En la parte cliente de la aplicación, se puede utilizar la clase DataInputStream para crear un stream de entrada que esté listo a recibir todas las respuestas que el servidor le envíe.

DataInputStream entrada;

try {
entrada = new DataInputStream( miCliente.getInputStream() );
} catch (IOException e) {
System.out.println( e );
}

La clase DataInputStream permite la lectura de líneas de texto y tipos de datos primitivos de Java de un modo altamente portable; dispone de métodos para leer todos esos tipos como: read(), readChar(), readInt(), readDouble() y readLine(). Deberemos utilizar la función que creamos necesaria dependiendo del tipo de dato que esperemos recibir del servidor.

En el lado del servidor, también usaremos DataInputStream, pero en este caso para  recibir  las  entradas  que  se  produzcan  de  los  clientes  que  se  hayan conectado:

DataInputStream entrada;

try {
entrada = new DataInputStream( socketServicio.getInputStream());
} catch( IOException e ) {
System.out.println( e );
}

1.1.6.        Creación de Streams de Salida
En el lado del cliente, podemos crear un stream de salida para enviar información al socket del servidor utilizando las clases PrintStream o DataOutputStream:

PrintStream salida;

try {
salida = new PrintStream( miCliente.getOutputStream() );
} catch( IOException e ) {
System.out.println( e );
}

La clase PrintStream tiene métodos para la representación textual de todos los datos primitivos  de Java. Sus métodos write y println() tienen una especial importancia  en  este  aspecto.  No  obstante,  para  el  envío  de  información  al servidor también podemos utilizar DataOutputStream:

DataOutputStream salida;

try {
salida = new DataOutputStream( miCliente.getOutputStream() );
} catch( IOException e ) {
System.out.println( e );
}

La clase DataOutputStream permite escribir cualquiera de los tipos primitivos de Java, muchos de sus métodos escriben un tipo de dato primitivo en el stream de salida. De todos esos métodos, el más útil quizás sea writeBytes().

En el lado del servidor, podemos utilizar la clase PrintStream para enviar información al cliente:

PrintStream salida;

try {
salida = new PrintStream (socketServicio.getOutputStream());
} catch( IOException e ) {
System.out.println( e );
}

Pero también podemos utilizar la clase DataOutputStream como en el caso de envío de información desde el cliente.

1.1.7.        Cierre de Sockets
Siempre deberemos cerrar los canales de entrada y salida que se hayan abierto durante la ejecución de la aplicación. En la parte del cliente:

try {
salida.close();
entrada.close();
miCliente.close();
} catch( IOException e ) {
System.out.println( e );
}

Y en la parte del servidor:

try {
salida.close(); entrada.close();
socketServicio.close();
miServicio.close();
} catch( IOException e ) {
System.out.println( e );
}

No hay comentarios:

Publicar un comentario