Está en la página 1de 31

INSTITUTO TECNOLOGICO NACIONAL DE MÉXICO, CAMPUS

ORIZABA

RMI (REMOTE METHOD INVOCATION)

MATERIA
Programación en ambiente cliente/servidor

PRESENTA

Vasquez Carmona Oscar

DOCENTE
Altamirano Cruz Luis Enrique

CARRERA
INGENERIA INFORMATICA

10° SEMESTRE
2.1. Concepto de socket.
Un socket (enchufe), es un método para la comunicación entre un programa
del cliente y un programa del servidor en una red, se define, por tanto, como el
punto final en una conexión.

Este mecanismo surge a principios de los 80 con el sistema Unix de Berkeley,


para proporcionar un medio de comunicación entre procesos y presentan la
misma funcionalidad que tiene la comunicación por correo o por teléfono (de
un buzón se extraen mensajes completos, mientras que el teléfono permite el
envío de flujos de información que no tienen una estructura claramente
definida), es decir permiten que un proceso hable (emita o reciba información)
con otro incluso estando estos en distintas máquinas. Esta característica de
ínterconectividad hace que el concepto de socket sea de gran utilidad.

Un socket queda definido por un par de direcciones IP local y remota, un


protocolo de transporte y un par de números de puerto local y remoto. Para
que dos programas puedan comunicarse entre sí es necesario que se cumplan
ciertos requisitos:

 Que un programa sea capaz de localizar al otro.


 Que ambos programas sean capaces de intercambiarse cualquier
secuencia de octetos, es decir, datos relevantes a su finalidad.

Para ello son necesarios los tres recursos que originan el concepto de socket:

 Un protocolo de comunicaciones, que permite el intercambio de octetos.


 Un par de direcciones del Protocolo de Red (Dirección IP, si se utiliza
el Protocolo TCP/IP), que identifica la computadora de origen y la
remota.
 Un par de números de puerto, que identifica a un programa dentro de
cada computadora.

Los sockets permiten implementar una arquitectura cliente-servidor o peer to


peer. La comunicación debe ser iniciada por uno de los programas que se
denomina programa cliente. El segundo programa espera a que otro inicie la
comunicación, por este motivo se denomina programa servidor.

Un socket es un proceso o hilo existente en la máquina cliente y en la máquina


servidora, que sirve en última instancia para que el programa servidor y el
cliente lean y escriban la información. Esta información será la transmitida por
las diferentes capas de red.

Cuando un cliente conecta con el servidor se crea un nuevo socket, de esta


forma, el servidor puede seguir esperando conexiones en el socket principal y
comunicarse con el cliente conectado, de igual manera se establece un socket
en el cliente en un puerto local.

Una aplicación servidor normalmente escucha por un puerto específico


esperando una petición de conexión de un cliente, una vez que se recibe, el
cliente y el servidor se conectan de forma que les sea posible comunicarse
entre ambos. Durante este proceso, el cliente es asignado a un número de
puerto, mediante el cual envía peticiones al servidor y recibe de este las
respuestas correspondientes.

Similarmente, el servidor obtiene un nuevo número de puerto local que le


servirá para poder continuar escuchando cada petición de conexión del puerto
original. De igual forma une un socket a este puerto local.

URL s y URLConnection s proporcionan un mecanismo relativamente alto


nivel para acceder a recursos en Internet. A veces, sus programas requieren
comunicación de red de nivel inferior, por ejemplo, cuando desea escribir una
aplicación cliente-servidor.

En las aplicaciones cliente-servidor, el servidor proporciona algún servicio,


como el procesamiento de consultas de bases de datos o el envío de precios de
acciones actuales. El cliente utiliza el servicio proporcionado por el servidor,
ya sea mostrando los resultados de la consulta de la base de datos al usuario o
haciendo recomendaciones de compra de acciones a un inversionista. La
comunicación que se produce entre el cliente y el servidor debe ser confiable.
Es decir, no se puede eliminar ningún dato y debe llegar al lado del cliente en
el mismo orden en que el servidor lo envió.

TCP proporciona un canal de comunicación punto a punto confiable que las


aplicaciones cliente-servidor en Internet utilizan para comunicarse entre sí.
Para comunicarse a través de TCP, un programa cliente y un programa
servidor establecen una conexión entre sí. Cada programa enlaza un socket a
su extremo de la conexión. Para comunicarse, el cliente y el servidor leen y
escriben en el socket vinculado a la conexión.
Un socket es un punto final de un enlace de comunicación de dos vías entre dos
programas que se ejecutan en la red. Las clases de socket se utilizan para
representar la conexión entre un programa cliente y un programa servidor. El
paquete java.net proporciona dos clases, Socket y ServerSocket, que
implementan el lado del cliente de la conexión y el lado del servidor de la
conexión, respectivamente.

Normalmente, un servidor se ejecuta en una computadora específica y tiene un


socket que está vinculado a un número de puerto específico. El servidor solo
espera, escuchando el socket de un cliente para realizar una solicitud de
conexión.

En el lado del cliente: el cliente conoce el nombre de host de la máquina en la


que se ejecuta el servidor y el número de puerto en el que el servidor está
escuchando. Para realizar una solicitud de conexión, el cliente intenta reunirse
con el servidor en la máquina y el puerto del servidor. El cliente también debe
identificarse con el servidor, por lo que se enlaza con un número de puerto
local que utilizará durante esta conexión. Esta es generalmente asignada por el
sistema.

Si todo va bien, el servidor acepta la conexión. Una vez aceptado, el servidor


obtiene un nuevo socket vinculado al mismo puerto local y también tiene su
punto final remoto configurado en la dirección y el puerto del cliente. Necesita
un nuevo zócalo para que pueda seguir escuchando el zócalo original en busca
de solicitudes de conexión mientras atiende las necesidades del cliente
conectado.
En el lado del cliente, si se acepta la conexión, se crea un socket con éxito y el
cliente puede usar el socket para comunicarse con el servidor.
El cliente y el servidor ahora pueden comunicarse escribiendo o leyendo desde
sus sockets.

Un socket es un punto final de un enlace de comunicación de dos vías entre


dos programas que se ejecutan en la red. Un socket está vinculado a un
número de puerto para que la capa TCP pueda identificar la aplicación que los
datos están destinados a enviar.

Un punto final es una combinación de una dirección IP y un número de


puerto. Cada conexión TCP puede identificarse de forma única por sus dos
puntos finales. De esa manera puede tener múltiples conexiones entre su host
y el servidor.

Los java.net paquete en la plataforma Java proporciona una clase, Socket ,


que implementa un lado de una conexión bidireccional entre su programa Java
y otro programa en la red. los Enchufe La clase se encuentra sobre una
implementación dependiente de la plataforma, ocultando los detalles de
cualquier sistema en particular de su programa Java. Usando el
java.net.Socket En lugar de confiar en el código nativo, sus programas Java
pueden comunicarse a través de la red de manera independiente de la
plataforma.

Adicionalmente, java.net incluye el ServerSocket clase, que implementa


un socket que los servidores pueden usar para escuchar y aceptar conexiones a
clientes. Esta lección le muestra cómo usar
el Enchufe y ServerSocket clases.
Los sockets no son más que puntos o mecanismos de comunicación entre
procesos que permiten que un proceso hable (emita o reciba información) con
otro proceso incluso estando estos procesos en distintas máquinas. Esta
característica de interconectividad entre máquinas hace que el concepto de
socket nos sirva de gran utilidad. Esta interfaz de comunicaciones es una de
las distribuciones de Berkeley al sistema UNIX, implementándose las
utilidades de interconectividad de este Sistema Operativo (rlogin, telnet,
ftp, ... ) usando sockets.

Un socket es al sistema de comunicación entre ordenadores lo que un buzón o


un teléfono es al sistema de comunicación entre personas: un punto de
comunicación entre dos agentes ( procesos o personas respectivamente ) por el
cual se puede emitir o recibir información.
La forma de referenciar un socket por los procesos implicados es mediante
undescriptor del mismo tipo que el utilizado para referenciar ficheros. Debido
a esta característica, se podrá realizar redirecciones de los archivos de E/S
estándar (descriptores 0,1 y 2) a los sockets y así combinar entre ellos
aplicaciones de la red. Todo nuevo proceso creado heredará, por tanto, los
descriptores de sockets de su padre.

La comunicación entre procesos a través de sockets se basa en la


filosofía CLIENTE-SERVIDOR: un proceso en esta comunicación actuará
de proceso servidor creando un socket cuyo nombre conocerá el proceso
cliente, el cual podrá "hablar" con el proceso servidor a través de la conexión
con dicho socket nombrado.

El proceso crea un socket sin nombre cuyo valor de vuelta es un descriptor


sobre el que se leerá o escribirá, permitiéndose una
comunicación bidireccional, característica propia de los sockets y que los
diferencia de los pipes, o canales de comunicación unidireccional entre
procesos de una misma máquina. El mecanismo de comunicación vía sockets
tiene los siguientes pasos:

1º) El proceso servidor crea un socket con nombre y espera la conexión.


2º) El proceso cliente crea un socket sin nombre.
3º) El proceso cliente realiza una petición de conexión al socket servidor.
4º) El cliente realiza la conexión a través de su socket mientras el
proceso servidor mantiene el socket servidor original con nombre.

Es muy común en este tipo de comunicación lanzar un proceso hijo, una vez
realizada la conexión, que se ocupe del intercambio de información con el
proceso cliente mientras el proceso padre servidor sigue aceptando
conexiones. Para eliminar esta característica se cerrará el descriptor del socket
servidor con nombre en cuanto realice una conexión con un proceso socket
cliente.

-Todo socket viene definido por dos características fundamentales:

- El tipo del socket, que indica la naturaleza del mismo, el tipo de


comunicación que puede generarse entre los sockets.

- El dominio del socket especifica el conjunto de sockets que pueden


establecer una comunicación con el mismo.

La programación en red siempre ha sido dificultosa, el programador debía de


conocer la mayoría de los detalles de la red, incluyendo el hardware utilizado,
los distintos niveles en que se divide la capa de red, las librerías necesarias
para programar en cada capa, etc.

Pero, la idea simplemente consiste en obtener información desde otra


maquina, aportada por otra aplicación software. Por lo tanto, de cierto modo
se puede reducir al mero hecho de leer y escribir archivos, con ciertas
salvedades.

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 los permisos del fichero o dispositivo que se
desea utilizar.

Una vez que el fichero o dispositivo se encuentra abierto, el proceso de


usuario realiza una o varias llamadas a Leer (read) y Escribir (write), para la
lectura y escritura de los datos.

El proceso de lectura toma los datos desde el objeto y los transfiere al proceso
de usuario, mientras que el de escritura los transfiere desde el proceso de
usuario al objeto. Una vez concluido el intercambio de información, el
proceso de usuario llamará a Cerrar (close) para informar al sistema operativo
que ha finalizado la utilización del fichero o dispositivo.
En Unix, un proceso tiene un conjunto de descriptores de entrada/salida desde
donde leer y por donde escribir. Estos descriptores pueden estar referidos a
ficheros, dispositivos, o canales de comunicaciones sockets.

El ciclo de vida de un descriptor, aplicado a un canal de comunicación (por


ejemplo, un socket), está determinado por tres fases:

- Creación, apertura del socket


- Lectura y Escritura, recepción y envío de datos por el socket
- Destrucción, cierre del socket

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.

Las operaciones IPC se basan en pares de sockets. Se intercambian


información transmitiendo datos a través de mensajes que circulan entre un
socket en un proceso y otro socket en otro proceso. Cuando los mensajes son
enviados, se encolan en el socket hasta que el protocolo de red los haya
transmitido. Cuando llegan, los mensajes son encolados en el socket de
recepción hasta que el proceso que tiene que recibirlos haga las llamadas
necesarias para recoger esos datos.

El lenguaje Java fue desarrollado por la empresa Sun MicroSystems hacia el


año 1990, mediante la creación de un grupo de trabajo en cuya cabeza estaba
James Gosling. Este grupo de trabajo fue ideado para desarrollar un sistema de
control de electrodomésticos y de PDAs o asistentes personales (pequeños
ordenadores) y que además tuviese la posibilidad de interconexión a redes de
ordenadores. Todo ello implicaba la creación de un hardware polivalente, un
sistema operativo eficiente (SunOS) y un lenguaje de desarrollo (Oak). El
proyecto concluyó dos años más tarde con un completo fracaso que condujo a
la disolución del grupo.

Pero el desarrollo del proyecto relativo al lenguaje oak siguió adelante gracias
entre otras cosas a la distribución libre del lenguaje por Internet mediante la
incipiente, por aquellos años, World Wide Web. De esta forma el lenguaje
alcanzó cierto auge y un gran número de programadores se encargaron de su
depuración así como de perfilar la forma y usos del mismo.
El nombre de Java, surgió durante una de las sesiones de brain storming que
se celebraban por el equipo de desarrollo del lenguaje. Hubo que cambiar el
nombre debido a que ya existía otro lenguaje con el nombre de oak.

Sun MicroSystems lanzó las primeras versiones de Java a principios de 1995,


y se han ido sucediendo las nuevas versiones durante estos últimos años,
fomentando su uso y extendiendo las especificaciones y su funcionalidad.

Una de las características más importantes de Java es su capacidad y, a la vez,


facilidad para realizar aplicaciones que funcionen en red. La mayoría de los
detalles de implementación a bajo nivel están ocultos y son tratados de forma
transparente por la JVM (Java Virtual Machine). Los programas son
independientes de la arquitectura y se ejecutan indistintamente en una gran
variedad de equipos con diferentes tipos de microprocesadores y sistemas
operativos.

2.2. Dominios y Tipos de sockets.


Tipos de sockets.
Define las propiedades de las comunicaciones en las que se ve envuelto un
socket, esto es, el tipo de comunicación que se puede dar entre cliente y
servidor. Estas pueden ser:
- Fiabilidad de transmisión.
- Mantenimiento del orden de los datos.
- No duplicación de los datos.
- El "Modo Conectado" en la comunicación.
- Envío de mensajes urgentes.

Los tipos disponibles son los siguientes:

* Tipo SOCK_DGRAM: sockets para comunicaciones en modo no


conectado, con envío de datagramas de tamaño limitado (tipo telegrama).
En dominios Internet como la que nos ocupa el protocolo del nivel de
transporte sobre el que se basa es el UDP.

* Tipo SOCK_STREAM: para comunicaciones fiables en modo


conectado, de dos vías y con tamaño variable de los mensajes de datos. Por
debajo, en dominios Internet, subyace el protocolo TCP.

* Tipo SOCK_RAW: permite el acceso a protocolos de más bajo nivel


como el IP (nivel de red)

* Tipo SOCK_SEQPACKET: tiene las características


del SOCK_STREAM pero además el tamaño de los mensajes es fijo.

Cada tipo de socket va a definir una serie de propiedades en función de las


comunicaciones en las cuales está implicado:

a) La fiabilidad de la transmisión. Ningún dato transmitido se pierde.

b) La conservación del orden de los datos. Los datos llegan en el orden


en el que han sido emitidos.

c) La no duplicación de datos. Sólo llega a destino un ejemplar de cada


dato emitido.

d) La comunicación en modo conectado. Se establece una conexión


entre dos puntos antes del principio de la comunicación (es decir, se
establece un circuito virtual). A partir de entonces, una emisión desde
un extremo está implícitamente destinada al otro extremo conectado.

e) La conservación de los límites de los mensajes. Los límites de los


mensajes emitidos se pueden encontrar en el destino.

f) El envío de mensajes (urgentes). Corresponde a la posibilidad de


enviar datos fuera del flujo normal, y por consecuencia accesibles
inmediatamente (datos fuera de flujo).

Cabe reseñar que un cauce de comunicación normal tiene las cuatro primeras
propiedades, pero no las dos últimas.

En cuanto a los tipos de sockets disponibles, se pueden considerar:


* SOCK_STREAM: Los sockets de este tipo permiten
comunicaciones fiables en modo conectado (propiedades a, b, c y
d) y eventualmente autorizan, según el protocolo aplicado los
mensajes fuera de flujo (propiedad f). El protocolo subyacente en
el dominio Internet es TCP. Se establece un circuito virtual
realizando una búsqueda de enlaces libres que unan los dos
ordenadores a conectar (parecido a lo que hace la red telefónica
conmutada para establecer una conexión entre dos teléfonos).
Una vez establecida la conexión, se puede proceder al envío
secuencial de los datos, ya que la conexión es permanente. Son
streams de bytes full-dúplex (similar a pipes). Un socket stream
debe estar en estado conectado antes de que se envíe o reciba en
él.

* SOCK_DGRAM: Corresponde a los sockets destinados a la


comunicación en modo no conectado para el envío de datagramas
de tamaño limitado. Las comunicaciones correspondientes tienen
la propiedad e. En el dominio Internet, el protocolo subyacente es
el UDP. Los datagramas no trabajan con conexiones
permanentes. La transmisión por los datagramas es a nivel de
paquetes, donde cada paquete puede seguir una ruta distinta, no
garantizándose una recepción secuencial de la información.

* SOCK_RAW: Permite el acceso a los protocolos de más bajo


nivel (por ejemplo, el protocolo IP en el dominio Internet). Su
uso está reservado al superusuario.

* SOCK_SEQPACKET: Corresponde a las comunicaciones que


poseen las propiedades a, b, c, d y e. Estas comunicaciones se
encuentran en el dominio XNS.

Los dos tipos de sockets más utilizados son los dos primeros.

El dominio de un socket.
Indica el formato de las direcciones que podrán tomar los sockets y los
protocolos que soportarán dichos sockets.
La estructura genérica es

struct sockaddr {
u__short sa__family; /* familia */
char sa__data[14]; /* dirección */
};

Pueden ser:

* Dominio AF_UNIX ( Address Family UNIX ):

El cliente y el servidor deben estar en la misma máquina. Debe


incluirse el fichero cabecera /usr/include/sys/un.h. La estructura de una
dirección en este dominio es:
struct sockaddr__un {
short sun__family; /* en este caso AF_UNIX */
char sun__data[108]; /* dirección */
};

* Dominio AF_INET ( Address Family INET ):


El cliente y el servidor pueden estar en cualquier máquina de la red
Internet. Deben incluirse los ficheros cabecera /usr/include/netinet/in.h,
/usr/include/arpa/inet.h, /usr/include/netdb.h. La estructura de una
dirección en este dominio es:

struct in__addr {
u__long s__addr;
};

struct sockaddr__in {
short sin_family; /* en este caso AF_INET */
u__short sin_port; /* numero del puerto */
struct in__addr sin__addr; /* direcc Internet */
char sin_zero[8]; /* campo de 8 ceros */
};

Estos dominios van a ser los utilizados en xshine. Pero existen otros
como:
* Dominio AF_NS:
Servidor y cliente deben estar en una red XEROX.
* Dominio AF_CCITT:
Para protocolos CCITT, protocolos X25, ...

Una familia, o dominio de la conexión, agrupa todos aquellos sockets que


comparten características comunes. Especifica el formato de las direcciones
que se podrán dar al socket y los diferentes protocolos soportados por las
comunicaciones vía los sockets de este dominio.

Cada protocolo, a la hora de referirse a un nodo de la red, implementa un


mecanismo de direccionamiento. La dirección distingue de forma inequívoca a
cada nodo u ordenador, y es utilizada para encaminar los datos desde el nodo
origen hasta el nodo destino. Hay muchas llamadas al sistema que necesitan
un puntero a una estructura de dirección de socket para trabajar. La siguiente
estructura genérica, se utiliza para describir las diferente primitivas.

struct sockaddr {

u_short sa_family; /* familia de sockets; se emplean


constantes de la forma AF_xxx */

char sa_data[14]; /* 14 bytes que contienen la


dirección; su significado depende de la familia de
sockets que se emplee */

};

Pero en el caso de que estemos tratando con una aplicación particular, esta
estructura se deberá reemplazar por la estructura correspondiente del dominio
de comunicaciones utilizado, ya que por desgracia, no todas las familias de
direcciones se ajustan a la estructura genérica descrita.

No obstante, esta estructura genérica encaja en la estructura definida para la


familia AF_INET, perteneciente al dominio Internet, lo que hace que el
software de TCP/IP trabaje correctamente aunque el programador use la
estructura genérica anterior, puesto que ambas tienen el mismo número de
bytes. A continuación, pasamos a describir esta estructura del dominio
Internet.
struct sockaddr_in {

short sin_family; /* la familia de la dirección


AF_INET*/

u_short sin_port; /* 16 bits con el número del


puerto */

u_long sin_addr; /* 32 bits con la dirección


Internet (identificación de la red y del host)*/

char sin _zero[8]; /* 8 bytes no usados */

Se puede observar que en la dirección Internet el campo sin_family es


equivalente al campo sa_family de la dirección genérica y que los campos
sin_port, sin_addr y sin_zero cumplen la misma función que el campo
sa_addr.

También podemos ver el dominio UNIX (AF_UNIX), donde los sockets son
locales al sistema en el cual han sido definidos. Permiten la comunicación
interna de procesos, y su designación se realiza por medio de una referencia
UNIX.

struct sockaddr_un {

short sun_family; /* dominio UNIX:


AF_UNIX */

char sun_data[108]; /* path */

};

Estas direcciones se corresponden en realidad con paths de ficheros, y su


longitud (110 bytes) es superior a los 16 bytes que de forma estándar tienen
las direcciones del resto de familias. Esto es posible debido a que esta familia
se usa para comunicar procesos ejecutados bajo control de la misma máquina,
no teniendo así que acceder a la red. Otros dominios son:

AF_NS /* protocolos XEROX NS */

AF_CCITT /* protocolos CCITT, protocolos X.25, etc. */


AF_SNA /* IBM SNA */

AF_DECnet /* DECnet */

2.3. Creación/ implementación y supresión de sockets.


Apertura de Sockets
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 en donde estamos intentando abrir
laconexión y numeroPuerto es el puerto (un número) del servidor que está
corriendo sobre el cualnos queremos conectar. Cuando se selecciona un
número de puerto, se debe tener en cuenta que los puertos en el rango 0-1023
están reservados para usuarios con muchos privilegios (superusuarios o root).
Estos puertos son los que utilizan los servicios estándar del sistema como
email, ftp o http. 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 );
}
Si estamos programando un SERVIDOR, la forma de apertura del socket es la
quemuestra 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
objetosocket 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 );
}

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 );
}
Creación de Streams de Salida
En el lado del CLIENTE, podemos crear un stream de salida para enviar
información alsocket 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
datosprimitivos 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 utilizarDataOutputStream:

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() );

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 );
}

Es importante destacar que el orden de cierre es relevante. Es decir, se deben


cerrar primero los streams relacionados con un socket antes que el propio
socket, ya que de esta forma evitamos posibles errores de escrituras o lecturas
sobre descriptores ya cerrados.

Clases útiles en comunicaciones


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 la 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 servidorTCP/IP.

NetworkClient
Una clase creada para implementar métodos y variables utilizadas en la
creación de un clienteTCP/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 ser la implementación de un corta fuegos (TCP
es un protocolo no seguro), o acceder a equipos especiales (como un lector de
código de barras o un GPS diferencial), necesitaremos nuestra propia clase
Socket.

2.4. Desarrollo del lado del servidor con sockets.


Escribiendo el lado del servidor de un socket
Esta sección le muestra cómo escribir un servidor y el cliente que lo
acompaña. El servidor en el par cliente / servidor sirve para hacer chistes de
Knock Knock. Las bromas de Knock Knock son preferidas por los niños y
suelen ser vehículos para malos juegos de palabras. Van así:
Servidor : "Knock knock!"
Cliente : "¿Quién está ahí?"
Servidor : "Dexter".
Cliente : "Dexter who?"
Servidor : "Dexter salas con ramas de acebo".
Cliente : "Groan".
El ejemplo consta de dos programas Java que se ejecutan de forma
independiente: el programa cliente y el programa servidor. El programa
cliente es implementado por una sola clase , KnockKnockClient , y es muy
similar a la EchoClient Ejemplo de la sección anterior. El programa del
servidor es implementado por dos clases: KnockKnockServer y
KnockKnockProtocol , KnockKnockServer contiene el principal Método
para el programa del servidor y realiza el trabajo de escuchar el puerto,
establecer conexiones y leer y escribir en el
socket. KnockKnockProtocol Sirve los chistes. Realiza un seguimiento de
la broma actual, el estado actual (mandó a golpear, envió una pista, etc.) y
devuelve las distintas partes de texto de la broma según el estado actual. Este
objeto implementa el protocolo, el lenguaje que el cliente y el servidor han
acordado usar para comunicarse.
La siguiente sección examina en detalle cada clase, tanto en el cliente como en
el servidor, y luego le muestra cómo ejecutarlas.
El servidor de Knock Knock
Esta sección describe el código que implementa el programa del servidor
Knock Knock. Aquí está la fuente completa para
el KnockKnockServer clase.
El programa servidor comienza creando una nueva ServerSocket objeto
para escuchar en un puerto específico (consulte la declaración en negrita en el
siguiente segmento de código). Al escribir un servidor, elija un puerto que aún
no esté dedicado a algún otro servicio. KnockKnockServer escucha en el
puerto 4444 ya 4 pasa a ser mi número favorito y el puerto 4444 no está
siendo utilizado para cualquier otra cosa en mi entorno:
tratar {
serverSocket = nuevo ServerSocket (4444);
}
captura (IOException e) {
System.out.println ("No se pudo escuchar en el puerto: 4444");
System.exit (-1);
}
ServerSocket es un java.net Clase que proporciona una implementación
independiente del sistema del lado del servidor de una conexión de socket
cliente / servidor. El constructor para ServerSocket emite una excepción si no
puede escuchar en el puerto especificado (por ejemplo, el puerto ya se está
utilizando). En este caso, la KnockKnockServer no tiene más remedio que
salir.
Si el servidor se enlaza exitosamente a su puerto, entonces
el ServerSocket el objeto se creó correctamente y el servidor continúa con
el siguiente paso: aceptar una conexión de un cliente (se muestra en negrita):
Socket clientSocket = null;
tratar {
clientSocket = serverSocket.accept ();
}
captura (IOException e) {
System.out.println ("Error de aceptación: 4444");
System.exit (-1);
}
los aceptar El método espera hasta que un cliente se inicie y solicite una
conexión en el host y el puerto de este servidor (en este ejemplo, el servidor se
está ejecutando en la máquina hipotética Taranis en el puerto 4444). Cuando
se solicita una conexión y se establece con éxito, el método de aceptación
devuelve un nuevo Enchufe objeto que está vinculado al mismo puerto local
y tiene su dirección remota y su puerto remoto establecidos en el del cliente.
El servidor puede comunicarse con el cliente a través de este
nuevo Enchufe y siga escuchando las solicitudes de conexión del cliente en
el original ServerSocket Esta versión particular del programa no escucha
más solicitudes de conexión de clientes. Sin embargo, una versión modificada
del programa se proporciona en Apoyo a múltiples clientes .
Una vez que el servidor establece con éxito una conexión con un cliente, se
comunica con el cliente utilizando este código:
PrintWriter out = new PrintWriter (clientSocket.getOutputStream (), true);
BufferedReader in =
nuevo BufferedReader (nuevo InputStreamReader
(clientSocket.getInputStream ()));
String inputLine, outputLine;

// iniciar la conversación con el cliente


KnockKnockProtocol kkp = nuevo KnockKnockProtocol ();
outputLine = kkp.processInput (null);
out.println (outputLine);

while ((inputLine = in.readLine ())! = null) {


outputLine = kkp.processInput (inputLine);
out.println (outputLine);
if (outputLine.equals ("Bye."))
descanso;
}
Esta código :
1. Obtiene el flujo de entrada y salida del socket y abre lectores y
escritores en ellos.
2. Inicia la comunicación con el cliente escribiendo en el socket (se
muestra en negrita).
3. Se comunica con el cliente leyendo y escribiendo en el socket
(el mientras lazo).
El paso 1 ya es familiar. El paso 2 se muestra en negrita y vale unos pocos
comentarios. Las declaraciones en negrita en el segmento de código anterior
inician la conversación con el cliente. El código crea
un KnockKnockProtocol objeto: el objeto que realiza un seguimiento de la
broma actual, el estado actual dentro de la broma, etc.
Después de la KnockKnockProtocol Se crea, el código
llama. KnockKnockProtocol 's proceso de entrada Método para obtener el
primer mensaje que el servidor envía al cliente. Para este ejemplo, lo primero
que dice el servidor es "Knock! Knock!" A continuación, el servidor escribe la
información al PrintWriter conectado al socket del cliente, enviando así el
mensaje al cliente.
El paso 3 se codifica en el mientras lazo. Mientras el cliente y el servidor
aún tengan algo que decirse, el servidor lee y escribe en el socket, enviando
mensajes entre el cliente y el servidor.
El servidor inició la conversación con un "Knock! Knock!" así que luego el
servidor debe esperar a que el cliente diga "¿Quién está ahí?" Como resultado,
el bucle while se repite en una lectura desde el flujo de entrada. El método
readLine espera hasta que el cliente responda escribiendo algo en su flujo de
salida (el flujo de entrada del servidor). Cuando el cliente responde, el
servidor pasa la respuesta del cliente a la KnockKnockProtocol objeto y
pregunta al KnockKnockProtocol objeto para una respuesta adecuada. El
servidor envía inmediatamente la respuesta al cliente a través del flujo de
salida conectado al zócalo, utilizando una llamada para imprimir. Si la
respuesta del servidor se genera desde el KnockKnockServer El objeto es
"Bye". esto indica que el cliente no quiere más chistes y el bucle se cierra.
los KnockKnockServer clase es un servidor de buen comportamiento, por lo
que las últimas líneas de esta sección de KnockKnockServer limpie
cerrando todos los flujos de entrada y salida, el socket del cliente y el socket
del servidor:
fuera.close ();
cercar();
clientSocket.close ();
serverSocket.close ();

El Protocolo de Knock Knock


Los KnockKnockProtocol La clase implementa el protocolo que el cliente y
el servidor utilizan para comunicarse. Esta clase realiza un seguimiento de
dónde se encuentran el cliente y el servidor en su conversación y presenta la
respuesta del servidor a las declaraciones del cliente.
los KnockKnockServer object contiene el texto de todos los chistes y se
asegura de que el cliente dé la respuesta adecuada a las declaraciones del
servidor. No sería bueno que el cliente dijera "Dexter who?" cuando el
servidor dice "Knock! Knock!"
Todos los pares cliente / servidor deben tener algún protocolo mediante el cual
se hablan entre sí; de lo contrario, los datos que pasan de un lado a otro no
tendrían sentido. El protocolo que utilizan sus propios clientes y servidores
depende completamente de la comunicación requerida por ellos para realizar
la tarea.

El cliente de Knock Knock


Los KnockKnockClient clase implementa el programa cliente que habla a
la KnockKnockServer . KnockKnockClient se basa en
el EchoClient Programa en la sección anterior, Leyendo desde y
escribiendo a un socket y debe ser algo familiar para usted. Pero repasaremos
el programa de todos modos y veremos lo que sucede en el cliente en el
contexto de lo que sucede en el servidor.
Cuando inicie el programa cliente, el servidor ya debería estar ejecutándose y
escuchando el puerto, esperando que un cliente solicite una conexión.
Entonces, lo primero que hace el programa cliente es abrir un socket que está
conectado al servidor que se ejecuta en el nombre de host y el puerto
especificado:
kkSocket = new Socket ("taranis", 4444);
out = new PrintWriter (kkSocket.getOutputStream (), true);
in = new BufferedReader (new InputStreamReader (kkSocket.getInputStream
()));
Al crear su zócalo, KnockKnockClient usa el nombre de host taranis , el
nombre de una máquina hipotética en nuestra red. Cuando escribe y ejecuta
este programa, cambie el nombre de host al nombre de una máquina en su red.
Esta es la máquina en la que ejecutará el KnockKnockServer .
los KnockKnockClient El programa también especifica el número de puerto
4444 al crear su socket. Esto es un número de puerto remoto, el número de
un puerto en la máquina del servidor, y es el puerto al
cual KnockKnockServer esta escuchando. El socket del cliente está
vinculado a cualquier disponible puerto local - un puerto en la máquina
cliente. Recuerda que el servidor también obtiene un nuevo socket. Ese zócalo
está vinculado al número de puerto local 4444 en su máquina. El zócalo del
servidor y el zócalo del cliente están conectados.
Luego viene el bucle while que implementa la comunicación entre el cliente y
el servidor. El servidor habla primero, por lo que el cliente debe escuchar
primero. El cliente hace esto leyendo el flujo de entrada adjunto al socket. Si
el servidor habla, dice "Adiós". y el cliente sale del bucle. De lo contrario, el
cliente muestra el texto en la salida estándar y luego lee la respuesta del
usuario, que escribe en la entrada estándar. Después de que el usuario escribe
un retorno de carro, el cliente envía el texto al servidor a través de la secuencia
de salida adjunta al socket.
while ((fromServer = in.readLine ())! = null) {
System.out.println ("Servidor:" + servidor);
if (fromServer.equals ("Bye."))
descanso;

fromUser = stdIn.readLine ();


if (fromUser! = null) {
System.out.println ("Cliente:" + de Usuario);
out.println (fromUser);
}
}
La comunicación finaliza cuando el servidor pregunta si el cliente desea
escuchar otra broma, el cliente dice que no y el servidor dice "Adiós".
En aras de un buen mantenimiento, el cliente cierra sus flujos de entrada y
salida y el socket:
fuera.close ();
cercar();
stdIn.close ();
kkSocket.close ();

Ejecutando los programas


Debe iniciar el programa del servidor primero. Para hacer esto, ejecute el
programa del servidor usando el intérprete de Java, como lo haría con
cualquier otra aplicación Java. Recuerde ejecutar el servidor en la máquina
que el programa cliente especifica cuando crea el socket.
A continuación, ejecute el programa cliente. Tenga en cuenta que puede
ejecutar el cliente en cualquier máquina de su red; no tiene que ejecutarse en
la misma máquina que el servidor.
Si es demasiado rápido, puede iniciar el cliente antes de que el servidor tenga
la oportunidad de inicializarse y comenzar a escuchar en el puerto. Si esto
sucede, verá un seguimiento de la pila del cliente. Si esto sucede, simplemente
reinicie el cliente.
Si olvida cambiar el nombre de host en el código fuente para
el KnockKnockClient programa, verá el siguiente mensaje de error:
No sé sobre el anfitrión: taranis
Para arreglar esto, modifique el KnockKnockClient programa y proporciona
un nombre de host válido para tu red. Recompila el programa cliente y vuelve
a intentarlo.
Si intenta iniciar un segundo cliente mientras el primer cliente está conectado
al servidor, el segundo cliente simplemente se bloquea. La siguiente
sección, Apoyo a múltiples clientes , habla sobre el soporte de múltiples
clientes.
Cuando obtenga con éxito una conexión entre el cliente y el servidor, verá el
siguiente texto que se muestra en su pantalla:
Servidor: Knock! ¡Golpe!
Ahora, debes responder con:
¿Quién está ahí?
El cliente se hace eco de lo que escribe y envía el texto al servidor. El servidor
responde con la primera línea de uno de los muchos chistes de Knock Knock
en su repertorio. Ahora su pantalla debe contener esto (el texto que escribió
está en negrita):
Servidor: Knock! ¡Golpe!
¿Quién está ahí?
Cliente: ¿Quién está ahí?
Servidor: Nabo
Ahora, respondes con:
Nabo quién? "
Nuevamente, el cliente hace eco de lo que escribe y envía el texto al servidor.
El servidor responde con la línea de perforación. Ahora tu pantalla debe
contener esto:
Servidor: Knock! ¡Golpe!
¿Quién está ahí?
Cliente: ¿Quién está ahí?
Servidor: Nabo
Nabo quien?
Cliente: Turnip who?
Servidor: ¡Apaga el calor, hace frío aquí! ¿Quieres otro? (s / n)
Si quieres escuchar otro chiste, escribe y ; si no, escribe n . Si escribes y ,
el servidor comienza de nuevo con "Knock! Knock!" Si escribes n , el
servidor dice "Bye". causando así que el cliente y el servidor salgan.
Si en algún momento comete un error de escritura, la KnockKnockServer el
objeto lo atrapa y el servidor responde con un mensaje similar a este:
Servidor: se supone que debes decir "¿Quién está ahí?"
El servidor vuelve a iniciar la broma de nuevo:
Servidor: inténtalo de nuevo. ¡Golpe! ¡Golpe!
Tenga en cuenta que el KnockKnockProtocol el objeto es particular sobre la
ortografía y la puntuación, pero no sobre el uso de mayúsculas.

2.5. Desarrollo del lado del cliente con sockets


Leyendo y escribiendo desde un socket
Veamos un ejemplo simple que ilustra cómo un programa puede establecer
una conexión a un programa de servidor usando el Enchufe clase y luego,
cómo el cliente puede enviar y recibir datos del servidor a través del socket.
El programa de ejemplo implementa un cliente, EchoClient , que se conecta al
servidor Echo. El servidor Echo simplemente recibe datos de su cliente y los
devuelve. El servidor Echo es un servicio bien conocido con el que los clientes
pueden encontrarse en el puerto 7.
EchoClient crea un socket para obtener una conexión con el servidor Echo.
Lee la entrada del usuario en la secuencia de entrada estándar y luego envía
ese texto al servidor Echo escribiendo el texto en el socket. El servidor repite
la entrada de nuevo a través del socket al cliente. El programa cliente lee y
muestra los datos que se le devuelven desde el servidor:

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

clase pública EchoClient {


public static void main (String [] args) lanza IOException {

Socket echoSocket = nulo;


PrintWriter out = null;
BufferedReader in = null;

tratar {
echoSocket = new Socket ("taranis", 7);
out = new PrintWriter (echoSocket.getOutputStream (), true);
in = new BufferedReader (new InputStreamReader (
echoSocket.getInputStream ()));
} catch (UnknownHostException e) {
System.err.println ("No sé sobre host: taranis.");
System.exit (1);
} captura (IOException e) {
System.err.println ("No se pudo obtener E / S para"
+ "la conexión a: taranis.");
System.exit (1);
}

BufferedReader stdIn = new BufferedReader (


nuevo InputStreamReader (System.in));
String userInput;

while ((userInput = stdIn.readLine ())! = null) {


out.println (userInput);
System.out.println ("echo:" + in.readLine ());
}

fuera.close ();
cercar();
stdIn.close ();
echoSocket.close ();
}
}
Tenga en cuenta que EchoClient escribe y lee desde su socket, enviando
datos al servidor Echo y recibiendo datos del mismo.
Veamos el programa e investiguemos las partes interesantes. Las tres
afirmaciones en el tratar bloque de la principal El método es crítico. Estas
líneas establecen la conexión de socket entre el cliente y el servidor y abren
una PrintWriter y un BufferedReader en el zócalo:
echoSocket = new Socket ("taranis", 7);
out = new PrintWriter (echoSocket.getOutputStream (), true);
in = new BufferedReader (new InputStreamReader
(echoSocket.getInputStream ()));
La primera declaración en esta secuencia crea una nueva Enchufe objeto y
lo nombra echoSocket . los Enchufe El constructor utilizado aquí requiere
el nombre de la máquina y el número de puerto al que desea conectarse. El
programa de ejemplo usa el nombre de host taranis. Este es el nombre de una
máquina hipotética en nuestra red local. Cuando escribe y ejecuta este
programa en su máquina, cambie el nombre del host al nombre de una
máquina en su red. Asegúrese de que el nombre que utiliza sea el nombre IP
completo de la máquina a la que desea conectarse. El segundo argumento es el
número de puerto. El número de puerto 7 es el puerto en el que escucha el
servidor Echo.
La segunda declaración obtiene el flujo de salida del socket y abre
un PrintWriter en eso. De manera similar, la tercera instrucción obtiene el
flujo de entrada del socket y abre un BufferedReader en eso. El ejemplo
utiliza lectores y escritores para que pueda escribir caracteres Unicode en el
socket.
Para enviar datos a través del socket al servidor, EchoClient simplemente
tiene que escribir al PrintWriter . Para obtener la respuesta del
servidor, EchoClient lee desde el BufferedReader . El resto del programa lo
consigue. Si aún no está familiarizado con las clases de E / S de la plataforma
Java, puede leer I / O básico .
La siguiente parte interesante del programa es la mientras lazo. El bucle lee
una línea a la vez desde el flujo de entrada estándar y lo envía inmediatamente
al servidor escribiéndolo en el PrintWriter conectado al zócalo:
String userInput;
while ((userInput = stdIn.readLine ())! = null) {
out.println (userInput);
System.out.println ("echo:" + in.readLine ());
}
La última afirmación en el mientras bucle lee una línea de información de
la BufferedReader conectado al zócalo. los readLine El método espera
hasta que el servidor devuelve la información a EchoClient .
Cuando readline devoluciones, EchoClient Imprime la información a la
salida estándar.
los mientras el bucle continúa hasta que el usuario escribe un carácter de
final de entrada. Es decir, EchoClient lee la entrada del usuario, la envía al
servidor Echo, obtiene una respuesta del servidor y la muestra hasta que llega
al final de la entrada. los mientras entonces el bucle termina y el programa
continúa, ejecutando las siguientes cuatro líneas de código:
fuera.close ();
cercar();
stdIn.close ();
echoSocket.close ();
Estas líneas de código entran en la categoría de limpieza. Un programa de
buen comportamiento siempre se limpia después de sí mismo, y este programa
tiene un buen comportamiento. Estas declaraciones cierran los lectores y
escritores conectados al socket y al flujo de entrada estándar, y cierran la
conexión del socket al servidor. El orden aquí es importante. Debe cerrar todas
las secuencias conectadas a un socket antes de cerrar el socket.
Este programa cliente es sencillo y simple porque el servidor Echo
implementa un protocolo simple. El cliente envía texto al servidor, y el
servidor lo repite. Cuando sus programas cliente están hablando con un
servidor más complicado como un servidor HTTP, su programa cliente
también será más complicado. Sin embargo, los conceptos básicos son muy
similares a los de este programa:
1. Abra un zócalo.
2. Abra un flujo de entrada y un flujo de salida al socket.
3. Lea y escriba en el flujo de acuerdo con el protocolo del servidor.
4. Cierra los arroyos.
5. Cierre el zócalo.
Solo el paso 3 difiere de cliente a cliente, dependiendo del servidor. Los otros
pasos siguen siendo en gran medida los mismos.

También podría gustarte