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í.
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.
- 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.
- 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.
- 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.
- 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.
- En primer lugar, esta clase extiende Thread para que sus objetos asuman todas las propiedades de Threads.
- 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. - 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:
- Establecer una conexión de socket
- 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?
- 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.
- Después de asignar el nuevo subproceso, el servidor a través de su bucle while vuelve a entrar en estado de aceptación.
- 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