Sockets TCP

Descripción del código relacionado con los comunicación a través de Sockets Java

El código completo lo puede encontrar en: https://github.com/jorgaf/programacionintegrativa/tree/main/sockets/src/ec/edu/utpl/pi/practice/sockets/practices/p2/tcp.

En el enalce anterior encontrará las clases: ClientSocketTCP.java y ServidorSocketTCP.java. Comience con la clase que implementa el servidor, es decir: ServidorSocketTCP.

El servidor

Imports

Los imports que se usaron pertenecen a los paquetes java.io y java.net. El listado de sentencias import es el siguiente:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

Las clases que pertenecen al paquete java.io se relacionan con el manejo de las excepciones y los flujos de entrada y salida. Mientras que las clases del paquete java.net están relacionadas con los sockets.

La clase:

public class ServidorSocketTCP {

Como puede ver la clase es "sencilla" no hay herencia de otras clases ni implementaciones de interfaces.

Excepciones

En la comunicación basada en sockets es bastante común que se presenten errores de entrada o salida debido al uso de una red de computadores, por lo tanto los errores y excepciones serán del tipo IOException.

Dentro de la clase se manejan las excepciones a nivel de método principal así:

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

Es decir las excepciones no son capturadas, sino que en caso de producirse alguna, provocará que el programa termine.

Sentencias

Para que el programa funcione es necesario crear un socket del tipo servidor y asociarlo una dirección IP (nombre del host o dirección IP + puerto). Lo primero se hace creando un objeto de la clase ServerSocket y lo segundo con uno de la clase InetSocketAddress.

ServerSocket serverSocket = new ServerSocket();
InetSocketAddress addr = new InetSocketAddress("localhost", 5555);

Los sockets de tipo servidor esperan solicitudes que provienen desde la red de telecomunión y ejecutan alguna acción basados en la solicitud.

Observe con la creación de la dirección IP del socket se usa el nombre del host y el puerto. Cuando se usa el nombre del host en lugar de la dirección IP, se intentará resolver o encontrar la dirección IP de se host. Además, los posibles valores para el puerto es un número comprendido entre 0 y 65535. Considera que al usar 0 como puerto, será el sistema quién seleccione un puerto temporal.

Una vez que se tienen ambos objetos, es necesario asociar el socket servidor con su dirección IP, es por ello que se usa la siguiente sentencia:

serverSocket.bind(addr);

El socket servidor está listo para aceptar una solicitud, hay que enfatizar que el programa aceptará sólo una solicitud y luego terminará. Cuando se acepta una solicitud se crear un nuevo Socket o canal que permitirá la comunicación entre cliente y servidor.

Socket clienteConexion = serverSocket.accept();

Con el canal de comunicación se crean los flujos tanto de entrada InputStream y de salida OutputStream a través de las sentencias:

InputStream inStream = clienteConexion.getInputStream();
OutputStream outStream = clienteConexion.getOutputStream();

Estos flujos se basan en el envío y recepción de bytes, es por ello que se crea un arreglo de bytes en donde se leerá el mensaje que envía el cliente, así:

byte[] mensaje = new byte[25];
inStream.read(mensaje);

Una vez que se recibe el mensaje se lo transforma desde un arreglo de bytes a un String, esto se hace con la sentencia:

System.out.printf("Mensaje recibido: %s\n", new String(mensaje));

Recuerde que en la programación existe una premisa máxima, todo recurso abierto debe ser cerrado. Las siguientes sentencias hacen eso con todos los recursos abiertos generalmente en el orden inverso a su creación, es decir el primer en abrirse será el último en cerrarse.

clienteConexion.close();
inStream.close();
outStream.close();
System.out.println("Cerrando el socket servidor");
serverSocket.close();

El cliente

Imports

Para esta clase los imports son bastante similares a los de la clase anterior, únicamente se omite el import de la clase ServerSocket que en este caso no se usa, ya que se trata del cliente.

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

La clase

En esta clase no se necesita de herencia ni de la implementación de interfaces.

public class ClientSocketTCP {

Excepciones

Las excepciones son las mismas que se encuentra en la clase servidor y se manejan de la misma forma, dejando que el método principal las lance.

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

Sentencias

Las primeras sentencias tienen que ver con la creación de objetos, el primero que pertenece a la clase Socket y la segunda la dirección del servidor. El código que se usó es el siguiente:

        Socket clienteSocket = new Socket();
        InetSocketAddress address = new InetSocketAddress("localhost", 5555);

Recuerde que el cliente necesita conectarse con el servidor, razón por la cual el objeto address debe "apuntar" hacia el servidor.

Una vez que tiene ambos objetos, es necesario que el socket cliente se conecte con el servidor, esto se consigue con la sentencia:

        clienteSocket.connect(address);

Una vez que el cliente establece conexión con el servidor, lo siguiente que se debe realizar es crear los flujos de entrada y salida, que usará el cliente, así:

        InputStream inStream = clienteSocket.getInputStream();
        OutputStream outStream = clienteSocket.getOutputStream();

InputStrem será el flujo de entrada, es decir, por ese flujo llegarían los datos que envía el servidor, mientras que OutputStream es el flujo de salida, ese flujo usa el cliente para enviar datos hacia el servidor. En este ejemplo no se utiliza el flujo de entrada, ya que el servidor no envía datos al cliente, sólo se usa el flujo de salida, debido a que el cliente envía datos hacia el servidor.

Lo siguiente que hace el código es enviar el texto Mensaje desde el cliente, pero antes transforma el mensaje en bytes. El código es el siguiente:

        outStream.write(mensaje.getBytes());

Esas serían las principales sentencias del cliente, lo que resta es cerrar los recursos abiertos, esto se consigue con las sentencias:

        outStream.close();
        inStream.close();
        System.out.println("Cerrando el socket cliente");
        clienteSocket.close();

Recuerde que primero debe ejecutar la clase servidor, para luego ejecutar la clase cliente.

Salida

Los programas generan las siguientes salidas:

Cuando se ejecuta el servidor se mostrará:

Creando socket servidor
Realizando el bind
Aceptando conexiones

El programa se detiene hasta que llega una conexión del cliente. Es momento de ejecutar el cliente, esa acción genera la siguiente salida:

Creando socket cliente
Estableciendo conexión
Enviando mensaje
Mensaje enviado
Cerrando el socket cliente
Terminado

La presentación de los mensajes es inmediata, recuerde que su computador es cliente y servidor a la vez, y los mensajes no deben viajar por Intenert para llegar desde el origen al destino.

Cuando el servidor recibe una solicitud, a la salida enterior se le agregan los siguientes mensajes:

Conexión recibida
Mensaje recibido: Mensaje desde el cliente
Cerrando el nuevo socket
Cerrando el socket servidor

No olvide que en la explicación que aquí se hizo del código se dejaron de lado las sentencias System.out.print* que generan esos mensajes, pero recuerde revisar el código completo para verlos.

Hasta aquí la explicación de ese código. Recuerde que debe tratar de implementarlo y ejecutarlo.

Last updated