Introducción a los subprocesos en la programación de sockets en Java

Prerrequisitos: Programación de sockets en Java

Este artículo asume que tiene conocimientos básicos de programación de sockets en Java y los detalles básicos del modelo cliente-servidor utilizado en la comunicación.

¿Por qué usar hilos en la programación de redes?

La razón es simple, no queremos que un solo cliente se conecte al servidor en un momento determinado, sino muchos clientes simultáneamente. Queremos que nuestra arquitectura admita varios clientes al mismo tiempo . Por esta razón, debemos usar subprocesos en el lado del servidor para que cada vez que llegue una solicitud de un cliente, se pueda asignar un subproceso separado para manejar cada solicitud.

Tomemos un ejemplo, supongamos que un servidor de fecha y hora está ubicado en un lugar, digamos X. Al ser un servidor genérico, no atiende a ningún cliente en particular, sino a un conjunto completo de clientes genéricos. Suponga también que en un momento determinado llegan dos requests al servidor. Con nuestro programa básico de servidor-cliente, la solicitud que llega incluso un nanosegundo primero podría conectarse al servidor y la otra solicitud sería rechazada ya que no se proporciona ningún mecanismo para manejar múltiples requests simultáneamente. Para superar este problema, utilizamos subprocesos en la programación de redes.
El siguiente artículo se centrará en la creación de un servidor de fecha y hora simple para manejar varias requests de clientes al mismo tiempo.

vista rápida

Como de costumbre, crearemos dos archivos java, Server.java y Client.java . El archivo del servidor contiene dos clases, a saber, Servidor (clase pública para crear un servidor) y ClientHandler (para manejar cualquier cliente que use subprocesos múltiples). El archivo de cliente contiene solo un cliente de clase pública (para crear un cliente). A continuación se muestra el diagrama de flujo de cómo estas tres clases interactúan entre sí.

Date-time-server-1

Programación del lado del servidor (Server.java)

  • Clase de servidor: los pasos involucrados en el lado del servidor son similares a los del artículo Programación de sockets en Java con un ligero cambio para crear el objeto de subproceso después de obtener los flujos y el número de puerto.
    1. Establecimiento de la conexión: el objeto de socket del servidor se inicializa y, dentro de un ciclo while, un objeto de socket acepta continuamente la conexión entrante.
    2. Obtención de los flujos: el objeto de flujo de entrada y el objeto de flujo de salida se extraen del objeto de socket de las requests actuales.
    3. Creación de un objeto controlador: después de obtener los flujos y el número de puerto, se crea un nuevo objeto clientHandler (la clase anterior) con estos parámetros.
    4. Invocación del método start() : El método start() se invoca en este objeto de subproceso recién creado.
  • Clase ClientHandler: como usaremos subprocesos separados para cada solicitud, comprendamos el funcionamiento y la implementación de la clase ClientHandler que extiende los subprocesos. Se creará una instancia de un objeto de esta clase cada vez que llegue una solicitud.
    1. En primer lugar, esta clase extiende Thread para que sus objetos asuman todas las propiedades de Threads.
    2. En segundo lugar, el constructor de esta clase toma tres parámetros, que pueden identificar de forma única cualquier solicitud entrante, es decir, un Socket , un DataInputStream para leer y un DataOutputStream para escribir. Cada vez que recibimos una solicitud del cliente, el servidor extrae su número de puerto, el objeto DataInputStream y el objeto DataOutputStream y crea un nuevo objeto de subproceso de esta clase e invoca el método start() en él.
      Nota: cada solicitud siempre tendrá un triplete de socket, flujo de entrada y flujo de salida. Esto garantiza que cada objeto de esta clase escriba en un flujo específico en lugar de en múltiples flujos.
    3. Dentro del método run() de esta clase, realiza tres operaciones: solicita al usuario que especifique si se necesita la hora o la fecha, lee la respuesta del objeto de flujo de entrada y, en consecuencia, escribe la salida en el objeto de flujo de salida.
// Java implementation of  Server side
// It contains two classes : Server and ClientHandler
// Save file as Server.java
  
import java.io.*;
import java.text.*;
import java.util.*;
import java.net.*;
  
// Server class
public class Server 
{
    public static void main(String[] args) throws IOException 
    {
        // server is listening on port 5056
        ServerSocket ss = new ServerSocket(5056);
          
        // running infinite loop for getting
        // client request
        while (true) 
        {
            Socket s = null;
              
            try 
            {
                // socket object to receive incoming client requests
                s = ss.accept();
                  
                System.out.println("A new client is connected : " + s);
                  
                // obtaining input and out streams
                DataInputStream dis = new DataInputStream(s.getInputStream());
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                  
                System.out.println("Assigning new thread for this client");
  
                // create a new thread object
                Thread t = new ClientHandler(s, dis, dos);
  
                // Invoking the start() method
                t.start();
                  
            }
            catch (Exception e){
                s.close();
                e.printStackTrace();
            }
        }
    }
}
  
// ClientHandler class
class ClientHandler extends Thread 
{
    DateFormat fordate = new SimpleDateFormat("yyyy/MM/dd");
    DateFormat fortime = new SimpleDateFormat("hh:mm:ss");
    final DataInputStream dis;
    final DataOutputStream dos;
    final Socket s;
      
  
    // Constructor
    public ClientHandler(Socket s, DataInputStream dis, DataOutputStream dos) 
    {
        this.s = s;
        this.dis = dis;
        this.dos = dos;
    }
  
    @Override
    public void run() 
    {
        String received;
        String toreturn;
        while (true) 
        {
            try {
  
                // Ask user what he wants
                dos.writeUTF("What do you want?[Date | Time]..\n"+
                            "Type Exit to terminate connection.");
                  
                // receive the answer from client
                received = dis.readUTF();
                  
                if(received.equals("Exit"))
                { 
                    System.out.println("Client " + this.s + " sends exit...");
                    System.out.println("Closing this connection.");
                    this.s.close();
                    System.out.println("Connection closed");
                    break;
                }
                  
                // creating Date object
                Date date = new Date();
                  
                // write on output stream based on the
                // answer from the client
                switch (received) {
                  
                    case "Date" :
                        toreturn = fordate.format(date);
                        dos.writeUTF(toreturn);
                        break;
                          
                    case "Time" :
                        toreturn = fortime.format(date);
                        dos.writeUTF(toreturn);
                        break;
                          
                    default:
                        dos.writeUTF("Invalid input");
                        break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
          
        try
        {
            // closing resources
            this.dis.close();
            this.dos.close();
              
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

Producción

A new client is connected : Socket[addr=/127.0.0.1,port=60536,localport=5056]
Assigning new thread for this client
Client Socket[addr=/127.0.0.1,port=60536,localport=5056] sends exit...
Closing this connection.
Connection closed

Programación del lado del cliente (Client.java)

La programación del lado del cliente es similar al programa de programación de socket general con los siguientes pasos:

  1. Establecer una conexión de socket
  2. Comunicación
// Java implementation for a client
// Save file as Client.java
  
import java.io.*;
import java.net.*;
import java.util.Scanner;
  
// Client class
public class Client 
{
    public static void main(String[] args) throws IOException 
    {
        try
        {
            Scanner scn = new Scanner(System.in);
              
            // getting localhost ip
            InetAddress ip = InetAddress.getByName("localhost");
      
            // establish the connection with server port 5056
            Socket s = new Socket(ip, 5056);
      
            // obtaining input and out streams
            DataInputStream dis = new DataInputStream(s.getInputStream());
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
      
            // the following loop performs the exchange of
            // information between client and client handler
            while (true) 
            {
                System.out.println(dis.readUTF());
                String tosend = scn.nextLine();
                dos.writeUTF(tosend);
                  
                // If client sends exit,close this connection 
                // and then break from the while loop
                if(tosend.equals("Exit"))
                {
                    System.out.println("Closing this connection : " + s);
                    s.close();
                    System.out.println("Connection closed");
                    break;
                }
                  
                // printing date or time as requested by client
                String received = dis.readUTF();
                System.out.println(received);
            }
              
            // closing resources
            scn.close();
            dis.close();
            dos.close();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

Producción :

What do you want?[Date | Time]..
Type Exit to terminate connection.
Date
2017/06/16
What do you want?[Date | Time]..
Type Exit to terminate connection.
Time
05:35:28
What do you want?[Date | Time]..
Type Exit to terminate connection.
Geeks
Invalid input
What do you want?[Date | Time]..
Type Exit to terminate connection.
Exit
Closing this connection : Socket[addr=localhost/127.0.0.1,port=5056,localport=60536]
Connection closed

¿Cómo funcionan estos programas juntos?

  1. Cuando un cliente, digamos client1, envía una solicitud para conectarse al servidor, el servidor asigna un nuevo hilo para manejar esta solicitud. El subproceso recién asignado tiene acceso a flujos para comunicarse con el cliente.
  2. Después de asignar el nuevo subproceso, el servidor a través de su bucle while vuelve a entrar en estado de aceptación.
  3. Cuando llega una segunda solicitud mientras la primera aún está en proceso, el servidor acepta esta solicitud y nuevamente asigna un nuevo hilo para procesarla. De esta forma, se pueden manejar múltiples requests incluso cuando algunas requests están en proceso.

¿Cómo probar el programa anterior en su sistema?

Guarde los dos programas en el mismo paquete o en cualquier lugar. Luego, primero ejecute Server.java seguido de Client.java. Puede copiar el programa cliente en dos tres archivos separados y ejecutarlos individualmente, o si tiene un IDE como eclipse, ejecute varias instancias desde el mismo programa. El resultado que se muestra arriba es de un programa de un solo cliente, se lograrán resultados similares si se utilizan varios clientes.
 
Siguiente: Aplicación de chat de subprocesos múltiples: Programación del lado del servidor , Programación del lado del cliente
 

Este artículo es una contribución de Rishabh Mahrsee . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *