Aplicación de chat de subprocesos múltiples en Java | Conjunto 1 (Programación del lado del servidor)

Requisitos previos: introducción de subprocesos en la programación de sockets
En el artículo anterior, se creó un servidor de fecha y hora simple que manejó varias requests de usuarios al mismo tiempo utilizando subprocesos. Explica los conceptos básicos de subprocesos en la programación de redes. Los mismos conceptos se pueden usar con una ligera modificación para ampliar la idea anterior y crear una aplicación de chat similar a Facebook Messenger, Whatsapp, etc. 
El siguiente artículo cubre la implementación de dicha aplicación con una explicación detallada, limitaciones y sus soluciones. 
En este conjunto, analizaremos la programación del lado del servidor (Server.java), la programación del lado del cliente (Client.java) se analiza en el Conjunto 2 .

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

1. Clase de servidor: la implementación del servidor principal es fácil y similar al artículo anterior. Los siguientes puntos ayudarán a comprender la implementación del servidor:

  1. El servidor ejecuta un bucle infinito para seguir aceptando requests entrantes.
  2. Cuando llega una solicitud, asigna un nuevo hilo para manejar la parte de comunicación.
  3. El servidor también almacena el nombre del cliente en un vector, para realizar un seguimiento de los dispositivos conectados. El vector almacena el objeto de hilo correspondiente a la solicitud actual. La clase auxiliar utiliza este vector para encontrar el nombre del destinatario al que se entregará el mensaje. Como este vector contiene todos los flujos, la clase de controlador puede usarlo para entregar mensajes a clientes específicos.
  4. Invoque el método start() .

2. Clase ClientHandler: similar al artículo anterior, creamos una clase de ayuda para manejar varias requests. Esta vez, junto con el socket y las secuencias, introducimos una variable de nombre. Esto contendrá el nombre del cliente que está conectado al servidor. Los siguientes puntos ayudarán a comprender la implementación de ClientHandler:

  • Cada vez que el controlador recibe una string, la divide en la parte del mensaje y del destinatario. Utiliza Stringtokenizer para este propósito con ‘#’ como delimitador. Aquí se supone que la string siempre tiene el formato: 
message # recipient
  • Luego busca el nombre del destinatario en la lista de clientes conectados, almacenada como un vector en el servidor. Si encuentra el nombre de los destinatarios en la lista de clientes, reenvía el mensaje en su flujo de salida con el nombre del remitente antepuesto al mensaje. 

Java

// Java implementation of  Server side
// It contains two classes : Server and ClientHandler
// Save file as Server.java
 
import java.io.*;
import java.util.*;
import java.net.*;
 
// Server class
public class Server
{
 
    // Vector to store active clients
    static Vector<ClientHandler> ar = new Vector<>();
     
    // counter for clients
    static int i = 0;
 
    public static void main(String[] args) throws IOException
    {
        // server is listening on port 1234
        ServerSocket ss = new ServerSocket(1234);
         
        Socket s;
         
        // running infinite loop for getting
        // client request
        while (true)
        {
            // Accept the incoming request
            s = ss.accept();
 
            System.out.println("New client request received : " + s);
             
            // obtain input and output streams
            DataInputStream dis = new DataInputStream(s.getInputStream());
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
             
            System.out.println("Creating a new handler for this client...");
 
            // Create a new handler object for handling this request.
            ClientHandler mtch = new ClientHandler(s,"client " + i, dis, dos);
 
            // Create a new Thread with this object.
            Thread t = new Thread(mtch);
             
            System.out.println("Adding this client to active client list");
 
            // add this client to active clients list
            ar.add(mtch);
 
            // start the thread.
            t.start();
 
            // increment i for new client.
            // i is used for naming only, and can be replaced
            // by any naming scheme
            i++;
 
        }
    }
}
 
// ClientHandler class
class ClientHandler implements Runnable
{
    Scanner scn = new Scanner(System.in);
    private String name;
    final DataInputStream dis;
    final DataOutputStream dos;
    Socket s;
    boolean isloggedin;
     
    // constructor
    public ClientHandler(Socket s, String name,
                            DataInputStream dis, DataOutputStream dos) {
        this.dis = dis;
        this.dos = dos;
        this.name = name;
        this.s = s;
        this.isloggedin=true;
    }
 
    @Override
    public void run() {
 
        String received;
        while (true)
        {
            try
            {
                // receive the string
                received = dis.readUTF();
                 
                System.out.println(received);
                 
                if(received.equals("logout")){
                    this.isloggedin=false;
                    this.s.close();
                    break;
                }
                 
                // break the string into message and recipient part
                StringTokenizer st = new StringTokenizer(received, "#");
                String MsgToSend = st.nextToken();
                String recipient = st.nextToken();
 
                // search for the recipient in the connected devices list.
                // ar is the vector storing client of active users
                for (ClientHandler mc : Server.ar)
                {
                    // if the recipient is found, write on its
                    // output stream
                    if (mc.name.equals(recipient) && mc.isloggedin==true)
                    {
                        mc.dos.writeUTF(this.name+" : "+MsgToSend);
                        break;
                    }
                }
            } catch (IOException e) {
                 
                e.printStackTrace();
            }
             
        }
        try
        {
            // closing resources
            this.dis.close();
            this.dos.close();
             
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

Producción:

New client request received : Socket[addr=/127.0.0.1,port=61818,localport=1234]
Creating a new handler for this client...
Adding this client to active client list
New client request received : Socket[addr=/127.0.0.1,port=61819,localport=1234]
Creating a new handler for this client...
Adding this client to active client list

Limitaciones:
aunque la implementación anterior del servidor logra manejar la mayoría de los escenarios, existen algunas deficiencias en el enfoque definido anteriormente.

  • Una observación clara de los programas anteriores es que si el número de clientes creciera, el tiempo de búsqueda aumentaría en la clase de controlador. Para evitar este aumento, se pueden utilizar dos mapas hash. Uno con el nombre como clave y el índice en la lista activa como valor. Otro con índice como clave y objeto controlador asociado como valor. De esta manera, podemos buscar rápidamente los dos hashmaps para encontrar el destinatario coincidente. Se deja a los lectores implementar este truco para aumentar la eficiencia de la implementación.
  • Otra cosa a tener en cuenta es que esta implementación no funciona bien cuando los usuarios se desconectan del servidor . Se generarían muchos errores porque la desconexión no se maneja en esta implementación. Se puede implementar fácilmente como en los ejemplos básicos de TCP anteriores. También se deja que el lector implemente esta función en el programa.

Hay una gran diferencia en el programa cliente (Client.java) con respecto a los artículos anteriores, por lo que se discutirá en el Conjunto 2 de esta serie.

Artículo relacionado: Aplicación de chat multihilo | conjunto 2

Este artículo es una contribución de Rishabh Mahrsee . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@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 *