Comunicaciones Con Java
Comunicaciones Con Java
Comunicaciones Con Java
Con
Java
Comunicaciones en Java
1. Comunicaciones en Unix
2. Sockets
o Sockets Stream
o Sockets Datagrama
o Sockets Raw
3. Diferencias entre Sockets Stream y Datagrama
4. Uso de Sockets
o Puertos y Servicios
o La clase URL
5. Dominios de Comunicaciones
o Dominio Unix
o Dominio Internet
6. Modelo de Comunicaciones con Java
o Apertura de Sockets
o Creación de Streams de Entrada
o Creación de Streams de Salida
o Cierre de Sockets
o Mínimo Cliente SMTP
o Servidor de Eco
o Mínimo Servidor TCP/IP
o Mínimo Cliente TCP/IP
o Servidor Simple de HTTP
o Red en Windows '95 (sin conexión)
1. Configuración del TCP/IP de Windows '95
2. Crear una entrada en la Red
3. Comprobación de la Red
4. Problemas más frecuentes
7. Clases Utiles en Comunicaciones
COMUNICACIONES EN UNIX
El sistema de Entrada/Salida de Unix sigue el paradigma que normalmente se designa
como Abrir-Leer-Escribir-Cerrar. Antes de que un proceso de usuario pueda realizar
operaciones de entrada/salida, debe hacer una llamada a Abrir (open) para indicar, y
obtener permisos para su uso, el fichero o dispositivo que quiere utilizar. Una vez que el
objeto está abierto, el proceso de usuario realiza una o varias llamadas a Leer (read) y
Escribir (write), para conseguir leer y escribir datos. Leer coge datos desde el objeto y
los transfiere al proceso de usuario, mientras que Escribir transfiere datos desde el
proceso de usuario al objeto. Una vez que todos estos intercambios de información
estén concluidos, el proceso de usuario llamará a Cerrar (close) para informar al
sistema operativo que ha finalizado la utilización del objeto que antes había abierto.
La interface IPC en Unix-BSD está implementada sobre los protocolos de red TCP y
UDP. Los destinatarios de los mensajes se especifican como direcciones de socket; cada
dirección de socket es un identificador de comunicación que consiste en una dirección
Internet y un número de puerto.
SOCKETS
Los sockets son puntos finales de enlaces de comunicaciones entre procesos. Los
procesos los tratan como descriptores de ficheros, de forma que se pueden intercambiar
datos con otros procesos transmitiendo y recibiendo a través de sockets.
Son un servicio orientado a conexión donde los datos se transfieren sin encuadrarlos en
registros o bloques. Si se rompe la conexión entre los procesos, éstos serán informados.
Son un servicio de transporte sin conexión. Son más eficientes que TCP, pero no está
garantizada la fiabilidad. Los datos se envían y reciben en paquetes, cuya entrega no
está garantizada. Los paquetes pueden ser duplicados, perdidos o llegar en un orden
diferente al que se envió.
Sockets Raw
Son sockets que dan acceso directo a la capa de software de red subyacente o a
protocolos de más bajo nivel. Se utilizan sobre todo para la depuración del código de los
protocolos.
En UDP, cada vez que se envía un datagrama, hay que enviar también el descriptor del
socket local y la dirección del socket que va a recibir el datagrama, luego éstos son más
grandes que los TCP. Como el protocolo TCP está orientado a conexión, tenemos que
establecer esta conexión entre los dos sockets antes de nada, lo que implica un cierto
tiempo empleado en el establecimiento de la conexión, que no existe en UDP.
Los datagramas son bloques de información del tipo lanzar y olvidar. Para la mayoría
de los programas que utilicen la red, el usar un flujo TCP en vez de un datagrama UDP
es más sencillo y hay menos posibilidades de tener problemas. Sin embargo, cuando se
requiere un rendimiento óptimo, y está justificado el tiempo adicional que supone
realizar la verificación de los datos, los datagramas son un mecanismo realmente útil.
En resumen, TCP parece más indicado para la implementación de servicios de red como
un control remoto (rlogin, telnet) y transmisión de ficheros (ftp); que necesitan
transmitir datos de longitud indefinida. UDP es menos complejo y tiene una menor
sobrecarga sobre la conexión; esto hace que sea el indicado en la implementación de
aplicaciones cliente/servidor en sistemas distribuidos montados sobre redes de área
local.
USO DE SOCKETS
Podemos pensar que un Servidor Internet es un conjunto de sockets que proporciona
capacidades adicionales del sistema, los llamados servicios.
Puertos y Servicios
Cada servicio está asociado a un puerto. Un puerto es una dirección numérica a través
de la cual se procesa el servicio. Sobre un sistema Unix, los servicios que proporciona
ese sistema se indican en el fichero /etc/services, y algunos ejemplos son:
daytime 13/udp
ftp 21/tcp
telnet 23/tcp telnet
smtp 25/tcp mail
http 80/tcp
La primera columna indica el nombre del servicio. La segunda columna indica el puerto
y el protocolo que está asociado al servicio. La tercera columna es un alias del servicio;
por ejemplo, el servicio smtp, también conocido como mail, es la implementación del
servicio de correo electrónico.
Las comunicaciones de información relacionada con Web tienen lugar a través del
puerto 80 mediante protocolo TCP. Para emular esto en Java, usaremos la clase Socket.
La fecha (daytime). Sin embargo, el servicio que coge la fecha y la hora del sistema,
está ligado al puerto 13 utilizando el protocolo UDP. Un servidor que lo emule en Java
usaría un objeto DatagramSocket.
La Clase URL
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
hubiesemos indicado 205.216.146.71, su dirección IP real.
La verdad es que la cosa es un poco más complicada que eso. Hay un servicio, el DNS
(Domain Name Service), que traslada www.yahoo.com a 205.216.146.71, lo que nos
permite teclear www.yahoo.com, en lugar de tener que recordar su dirección IP.
direccionIp[0] = 150
direccionIp[1] = 150
direccionIp[2] = 112
direccionIp[3] = 145
Una cosa interesante en este punto es que una red puede mapear muchas direcciones IP.
Esto puede ser necesario para un Servidor Web, como Yahoo, que tiene que soportar
grandes cantidades de tráfico y necesita más de una dirección IP para poder atender a
todo ese tráfico. El nombre interno para la dirección 205.216.146.71, por ejemplo, es
www7.yahoo.com. El DNS puede trasladar una lista de direcciones IP asignadas a
Yahoo en www.yahoo.com. Esto es una cualidad útil, pero por ahora abre un agujero en
cuestión de seguridad.
Ya conocemos la dirección IP, nos falta el número del puerto. Si no se indica nada, se
utilizará el que se haya definido por defecto en el fichero de configuración de los
servicios del sistema. En Unix se indican en el fichero /etc/services, en Windows-NT en
el fichero services y en otros sistemas puede ser diferente.
El puerto habitual de los servicios Web es el 80, así que si no indicamos nada,
entraremos en el servidor de Yahoo por el puerto 80. Si tecleamos la URL siguiente en
un navegador:
http://www.yahoo.com:80
también recibiremos la página principal de Yahoo. No hay nada que nos impida cambiar
el puerto en el que residirá el servidor Web; sin embargo, el uso del puerto 80 es casi
estándar, porque elimina pulsaciones en el teclado y, además, las direcciones URL son
lo suficientemente difíciles de recordar como para añadirle encima el número del
puerto.
ftp://ftp.microsoft.com
el puerto se derivará de ese protocolo. Así el puerto FTP de Microsoft es el 21, según su
fichero services. La primera parte, antes de los dos puntos, de la URL, indica el
protocolo que se quiere utilizar en la conexión con el servidor. El protocolo http
(HyperText Transmission Protocol), es el utilizado para manipular documentos Web. Y
si no se especifica ningún documento, muchos servidores están configurados para
devolver un documento de nombre index.html.
Con todo esto, Java permite los siguientes cuatro constructores para la clase URL:
Así que podríamos especificar todos los componenetes del URL como en:
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" );
DOMINIOS DE COMUNICACIONES
El mecanismo de sockets está diseñado para ser todo lo genérico posible. El socket por
sí mismo no contiene información suficiente para describir la comunicación entre
procesos. Los sockets operan dentro de dominios de comunicación, entre ellos se define
si los dos procesos que se comunican se encuentran en el mismo sistema o en sistemas
diferentes y cómo pueden ser direccionados.
Dominio Unix
Bajo Unix, hay dos dominios, uno para comunicaciones internas al sistema y otro para
comunicaciones entre sistemas.
Las comunicaciones intrasistema (entre dos procesos en el mismo sistema) ocurren (en
una máquina Unix) en el dominio Unix. Se permiten tanto los sockets stream como los
datagrama. Los sockets de dominio Unix bajo Solaris 2.x se implementan sobre TLI
(Transport Level Interface).
Dominio Internet
Los sockets stream permiten a los procesos comunicarse a través de TCP. Una vez
establecidas las conexiones, los datos se pueden leer y escribir a/desde los sockets como
un flujo (stream) de bytes. Algunas aplicaciones de servicios TCP son:
Los sockets datagrama permiten a los procesos utilizar el protocolo UDP para
comunicarse a y desde esos sockets por medio de bloques. UDP es un protocolo no
fiable y la entrega de los paquetes no está garantizada. Servicios UDP son:
Los sockets raw proporcionan acceso al Internet Control Message Protocol, ICMP, y se
utiliza para comunicarse entre varias entidades IP.
Hay una cuestión al respecto de los sockets, que viene impuesta por la implementación
del sistema de seguridad de Java. Actualmente, los applets sólo pueden establecer
conexiones con el nodo desde el cual se transfirió su código. Esto está implementado en
el JDK y en el intérprete de Java de Netscape. Esto reduce en gran manera la
flexibilidad de las fuentes de datos disponibles para los applets. El problema si se
permite que un applet se conecte a cualquier máquina de la red, es que entonces se
podrían utilizar los applets para inundar la red desde un ordenador con un cliente
Netscape del que no se sospecha y sin ninguna posibilidad de rastreo.
Apertura De Sockets
Socket miCliente;
miCliente = new Socket( "maquina",numeroPuerto );
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 );
}
Socket miServicio;
try {
miServicio = new ServerSocket( numeroPuerto );
} catch( IOException e ) {
System.out.println( e );
}
DataInputStream entrada;
try {
entrada = new DataInputStream( miCliente.getInputStream() );
} catch( IOException e ) {
System.out.println( e );
}
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 );
}
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 );
}
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 );
}
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 );
}
try {
salida.close();
entrada.close();
socketServicio.close();
miServicio.close();
} catch( IOException e ) {
System.out.println( e );
}
Vamos a desarrollar un mínimo cliente SMTP (simple mail transfer protocol), de forma
que podamos encapsular todos los datos en la aplicación. El código es libre de
modificación para las necesidades que sean; por ejemplo, una modificación interesante
sería que aceptase argumentos desde la línea de comandos y también capturase el texto
del mensaje desde la entrada estándar del sistema. Con estas modificaciones tendríamos
casi la misma aplicación de correo que utiliza Unix. Veamos el código de nuestro
cliente, smtpCliente.java:
import java.net.*;
import java.io.*;
class smtpCliente {
public static void main( String args[] ) {
Socket s = null;
DataInputStream sIn = null;
DataOutputStream sOut = null;
Servidor De Eco
import java.net.*;
import java.io.*;
class ecoServidor {
public static void main( String args[] ) {
ServerSocket s = null;
DataInputStream sIn;
PrintStream sOut;
Socket cliente = null;
String texto;
import java.awt.*;
import java.net.*;
import java.io.*;
class minimoServidor {
public static void main( String args[] ) {
ServerSocket s = (ServerSocket)null;
Socket s1;
String cadena = "Tutorial de Java!";
int longCad;
OutputStream s1out;
System.out.println( e );
}
El lado cliente de una aplicación TCP/IP descansa en la clase Socket. De nuevo, mucho
del trabajo necesario para establecer la conexión lo ha realizado la clase Socket. Vamos
a presentar ahora el código de nuestro cliente más simple, minimoCliente.java, que
encaja con el servidor presentado antes. El trabajo que realiza este cliente es que todo lo
que recibe del servidor lo imprime por la salida estándar del sistema.
import java.awt.*;
import java.net.*;
import java.io.*;
class minimoCliente {
public static void main( String args[] ) throws IOException {
int c;
Socket s;
InputStream sIn;
s.close();
}
}
import java.net.*;
import java.io.*;
import java.util.*;
tipo = mime_text_plain;
return( tipo );
}
}
// Esta clase sirve para que nos enteremos de lo que está haciendo
// nuestro servidor. En una implementación real, todos estos mensajes
// deberían registrarse en algún fichero
class HTTPlog {
public static void error( String entrada ) {
System.out.println( "Error: "+entrada );
}
if( !f.canRead() )
{
sout.write( HttpUtilidades.error( 404,
"Permiso Denegado",fich ) );
return;
}
sin.close();
} catch( IOException e ) {
HTTPlog.error( "Excepcion: "+e );
}
}
while( ( c = sin.read() ) != -1 )
{
switch( c ) {
case '\r':
break;
case '\n':
if( esCR )
return( new String( buf,0,0,pos ) );
esCR = true;
// Continúa, se ha puesto el primer \n en la cadena
default:
if( c != '\n' )
esCR = false;
buf[pos++] = (byte)c;
}
}
} catch( IOException e ) {
HTTPlog.error( "Error de Recepcion" );
}
return( null );
}
{
switch( tipoPeticion( peticion ) ) {
case RT_GET:
ctrlGet( peticion,sOut );
break;
case RT_UNSUP:
default:
ctrlNoSop( peticion,sOut );
break;
}
HTTPlog.peticion( peticion );
}
sIn.close();
sOut.close();
s.close();
}
}
}
Hay que seguir los pasos que vamos a relatar a continuación, suponemos que se está
ejecutando la versión española de Windows '95, en otras versiones puede que las
opciones tengan nombre diferente:
Comprobación de la red
En este instante, si todo ha ido bien, el ordenador está listo para funcionar como si
estuviera en red. Dos o más programas que se comuniquen en red a través de sockets
debería poder ejecutarse ahora dentro de los dominios del ordenador que acabamos de
configurar
Esto sucede porque tiene el DNS activado y no puede encontrar ese DNS o servidor de
direcciones, porque estamos solos en la red. Asegurarse de que en el paso 8 de la
Configuración la opción de DNS está deshabilitada.
Socket
Es el objeto básico en toda comunicación a través de Internet, bajo el protocolo TCP.
Esta clase proporciona métodos para la entrada/salida a través de streams que hacen la
lectura y escritura a través de sockets muy sencilla.
ServerSocket
Es un objeto utilizado en las aplicaciones servidor para escuchar las peticiones que
realicen los clientes conectados a ese servidor. Este objeto no realiza el servicio, sino
que crea un objeto Socket en función del cliente para realizar toda la comunicación a
través de él.
DatagramSocket
La clase de sockets datagrama puede ser utilizada para implementar datagramas no
fiables (sockets UDP), no ordenados. Aunque la comunicación por estos sockets es muy
rápida porque no hay que perder tiempo estableciendo la conexión entre cliente y
servidor.
DatagramPacket
Clase que representa un paquete datagrama conteniendo información de paquete,
longitud de paquete, direcciones Internet y números de puerto.
MulticastSocket
Clase utilizada para crear una versión multicast de las clase socket datagrama. Múltiples
clientes/servidores pueden transmitir a un grupo multicast (un grupo de direcciones IP
compartiendo el mismo número de puerto).
NetworkServer
Una clase creada para implementar métodos y variables utilizadas en la creación de un
servidor TCP/IP.
NetworkClient
Una clase creada para implementar métodos y variables utilizadas en la creación de un
cliente TCP/IP.
SocketImpl
Es un Interface que nos permite crearnos nuestro propio modelo de comunicación.
Tendremos que implementar sus métodos cuando la usemos. Si vamos a desarrollar una
aplicación con requerimientos especiales de comunicaciones, como pueden se la
import java.net.*;
import java.io.*;
import sun.net.*;
// Cerramos el socket
s.close();
}
}
import java.net.*;
import java.io.*;
import sun.net.*;
La salida que se producirá cuando ejecutemos primero el servidor y luego el cliente será
la misma que reproducimos a continuación:
%java clienteUDP
Recibido: 17 bytes
Datos: Hola Tutorial de Java!