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