Utilización de sockets en Java

62

La arquitectura de red más utilizada es la arquitectura llamada TCP/IP, que se basa en el modelo de red teórico OSI. Esta arquitectura toma su nombre del protocolo de transporte TCP y del protocolo de red IP. Al ser la arquitectura de red en la que se basa Internet, otras redes que se conectan a ella también emplean la misma arquitectura.

El modelo TCP/IP se divide en cuatro niveles, de los que nos interesan los tres niveles superiores (de menor a mayor):

  • Nivel de Interred; Su función es la encaminar los paquetes de datos hasta su destino final. Utiliza un sistema de direccionamiento que identifica de forma única a cada dispositivo de comunicaciones llamado dirección IP.
  • Nivel de transporte; Es el encargado de establecer el canal de comunicaciones extremo a extremo. Dispone de dos protocolos: TCP y UDP.
    • TCP es un protocolo orientado a conexión, es decir, espera confirmación de cada unidad de datos que envía. Debido a ello emisor y receptor deben establecer una conexión antes de empezar a enviarse datos deben cerrarla después de terminar la comunicación. Funciona análogamente como un servicio de telefonía.
    • UDP es un protocolo no orientado a conexión, sin garantía de entrega del mensaje pero más rápido que TCP. Por lo tanto, no es necesario establecer una conexión entre emisor y receptor. Funciona análogamente como un servicio de correos convencional.

Tanto TCP como UDP ofrecen sus servicios al nivel de aplicación a través de los Puntos de Acceso al Servicio (SAP), denominados también puertos y que se identifican con número comprendido entre 0 y 65535. De esta manera permiten multiplexar sobre un mismo host (dirección IP) múltiples servicios (aplicaciones).

  • Nivel de aplicación; Es el nivel más alto del modelo TCP/IP y lo forman las aplicaciones que ofrecen sus servicios al usuario o al sistema dónde están instaladas. Ejemplos de protocolos de este nivel son HTTP, FTP o SSH.

psp1

Definición de socket

El concepto de socket (conector) nos abstrae de la complejidad de protocolos que intervienen en el proceso de comunicación, reduciéndolo a la conexión entre extremos. Representa el extremo de un canal de comunicaciones establecido entre un emisor y un receptor. Requiere una aplicación en cada extremo que cree su socket y se conecten entre sí. Una vez establecida la conexión se crea como una tubería por donde fluyen los mensajes de un extremo a otro.

El envío y recepción de mensajes se reduce a escribir y leer respectivamente, en el socket correspondiente. Los hosts están identificados de forma única mediante una dirección IP y las aplicaciones de comunicaciones dentro de ese host tienen asociado un número de puerto único. Al binomio formado por la dirección IP más el número de puerto se le conoce como socket, que se representa como: dirección_IP:número_puerto. El socket es el direccionamiento de un host a nivel de transporte dentro del modelo TCP/IP.

psp2

Funcionamiento de los sockets

Normalmente, un servidor se ejecuta sobre una computadora específica y tiene un socket que responde en un puerto específico. El servidor únicamente espera, escuchando a través del socket a que un cliente haga una petición. En el lado del cliente, éste conoce el nombre de host de la máquina en la cual el servidor se encuentra ejecutando y el número de puerto en el cual el servidor está conectado. Para realizar una petición de conexión, el cliente intenta encontrar al servidor en la máquina servidor en el puerto especificado.

Si todo va bien, el servidor acepta la conexión. Además de aceptar, el servidor obtiene un nuevo socket sobre un puerto diferente. Esto se debe a que necesita un nuevo socket (y, en consecuencia, un número de puerto diferente) para seguir atendiendo desde el socket original para peticiones de conexión mientras atiende las necesidades del cliente que se conectó.

Por la parte del cliente, si la conexión es aceptada, un socket se crea de forma satisfactoria y puede usarlo para comunicarse con el servidor. Es importante darse cuenta que el socket en el cliente no está utilizando el número de puerto usado para realizar la petición al servidor. En lugar de éste, el cliente asigna un número de puerto local a la máquina en la cual está siendo ejecutado.

Tipos de sockets

  • Sockets Stream; Orientados a conexión, por tanto usan el protocolo TCP. Se utiliza para establecer una comunicación extremo a extremo y mantenerla hasta que se cierre.

Cliente

Servidor

Crea un socket. Socket cliente. Se asigna un número de puerto (el primero disponible) y la Ip de la máquina.

Crea un socket. Socket servidor. Asigna dirección y puerto (bind). Se pone en escucha de peticiones (listen)
Solicita conexión al socket servidor, especificando la IP y el puerto de éste (connect) El socket servidor acepta la petición del socket cliente (accept)
Se realiza el intercambio de mensajes (write y read) Se realiza el intercambio de mensajes (write y read)
Cierra la conexión (close)

Cierra la conexión (close). El Socket queda libre para otra comunicación.

  • Sockets Datagram; No orientados a conexión, usan UPD. Se pueden usar para enviar mensajes a multitud de receptores. No se distingue entre cliente y servidor. El proceso es similar al anterior pero sin necesidad de solicitar conexión ni aceptarla.

Cliente

Servidor

Crea un socket. Asigna dirección y puerto (bind)

Crea un socket. Asigna dirección y puerto (bind)
Se realiza el intercambio de mensajes (send y receive) Se realiza el intercambio de mensajes (send y receive)
Cierra la conexión (close)

Cierra la conexión (close)

Modelo de comunicaciones con Java

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.

psp3Clases de Java para la utilización de sockets

Java dispone de cuatro clases para las comunicaciones con sockets, todas ellas en el paquete java.net. Estas clases son:

  • Socket
  • ServerSocket
  • DatagramSocket
  • MultiSocket

Métodos de la clase Socket

Método

Descripción

Socket(String hostname, int port)

Crea un objeto Socket con el nombre de host y el número de puerto especificados como parámetros.
Void bind() Asocia el socket a una dirección local que es pasada como parámetro.
Void close() Cierra el socket.
Void connect() Conecta el socket a un servidor pasado como parámetro. Si también se le pasa un entero como valor indica el tiempo de espera para realizar la conexión.
InetAddress getInetAddress() Devuelve la dirección a la que está conectado el socket.
InputStream getInputStream() Devuelve el inputStream del socket.
InetAddress getLocalAddress() Obtiene la dirección local a la que está asociado el socket.
Int getLocalPort() Devuelve el puerto local al que está asociado el socket.
SocketAddress getLocalSocketAddress() Devuelve la dirección del punto final al que el socket está asociado o null si todavía no está asociado a ninguna dirección.
OutputStream getOutputStream() Devuelve el outputStream del socket.
Int getPort() Devuelve el puerto remoto al que está conectado el socket.
SocketAddress getRemoteSocketAddress()

Devuelve la dirección del punto final a la que el socket está conectado o null si no está conectado a ninguna dirección.

Métodos de la clase ServerSocket

Método

Descripción

ServerSocket()

Crea un ServerSocket que no está asociado a ninguna dirección.
ServerSocket(int port) Crea un ServerSocket especificando el puerto a través del cual se van a atender las peticiones de conexión.
ServerSocket (int port, int backlog) Crea un ServerSocket especificando un puerto concreto y el tiempo máximo de espera para aceptar un cliente.
ServerSocket (int port, int backlog, InetAddress bindAddr) Crea un ServerSocket especificando el puerto, el tiempo máximo de espera para aceptar un cliente y la dirección IP.
Socket accept() Escucha las solicitudes de conexión y las acepta.
Void bind() Asocia el ServerSocket a una dirección especificada.
Void close() Cierra el socket.
InetAddress getInetAddress() Devuelve la dirección local del ServerSocket.
Int getLocalPort() Devuelve el puerto en el que el socket está escuchando.
SocketAddress getLocalSocketAddress()

Devuelve la dirección del punto final al que el socket está asociado o null si todavía no está asociado a ninguna dirección.

Métodos de la clase DatagramSocket

Métodos

Descripción

DatagramSocket()

Construye un objeto DatagramSocket.
DatagramSocket(SocketAddress bindAddr) Construye un objeto DatagramSocket y lo asocia con la dirección y puerto especificados como parámetro.
Send(DatagramPacket p) Envía un datagrama.
Receive(DatagramPacket p) Recibe un datagrama.
Close()

Cierra el socket.

La clase MultiSocket es 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).

Apertura de sockets

Si estamos programando un CLIENTE, el socket se abre de la forma:

Socket miCliente = new Socket(«maquina»,numeroPuerto);

Donde maquina es el nombre de la máquina 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.

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 servicios estándar del sistema como SMTP, FTP o HTTP. Para las aplicaciones que se desarrollen, hay que asegurarse de seleccionar un puerto por encima del 1023.

Es conveniente meter la declaración y creación de un socket entre un bloque try-catch. De esta manera quedaría de la siguiente manera:

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

}

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.

Deja un comentario