Arquitectura de Servidores Servidores Concurrentes Servidores Iterativos Servidores con Estado Servidores sin Estado Qué pasa cuando varios clientes tratan de conectarse al mismo tiempo a un servidor Una forma es ir atendiéndolos de a uno en un ciclo: como en el programa que atiende pedidos de archivos Se acepta una conexión Se lee la petición Se lee desde el archivo y se escribe en el socket hasta encontrar una marca de fin de archivo A este tipo de servidores se les llama servidores iterativos El problema es que todo cliente tiene que esperar su turno para ser atendido Si uno de ellos pide un archivo muy grande los demás tienen que esperar La mayor parte de la espera es debido a operaciones de IO, hay capacidad de CPU ociosa ! Un servidor secuencial (iterativo) atendiendo a más de un cliente A CLIENT A SERVER A CLIENT 4444 A CLIENT Durante la conversación no puede oír por el puerto 4444 A CLIENT A SERVER A CLIENT 4444 A CLIENT Si el servicio consiste en transferir un archivo, el cliente debe digitar el nombre A CLIENT A SERVER A CLIENT 4444 A CLIENT Sólo después de efectuar la transmisión se pone a escuchar de nuevo por el 4444 A CLIENT A SERVER A CLIENT 4444 A CLIENT ¿Qué sucede si el servidor tiene que esperar mucho para que un cliente escriba el nombre de un archivo? A CLIENT A SERVER Timeout A CLIENT 4444 A CLIENT ArchServidor2 Un Servidor Concurrente Un servidor concurrente atiende a varios clientes al mismo tiempo. Más aún, mientras está atendiendo sigue escuchando El problema es que todo cliente tiene que esperar su turno para ser atendido. Si uno de ellos pide un archivo muy grande los demás tienen que esperar La mayor parte de la espera es debido a operaciones de IO, hay capacidad de CPU ociosa! Se trata de crear un nuevo proceso o línea de ejecución cada vez que un cliente “llega” a pedir un servicio. Servidores Comcurrentes: hay procesos separados para atender el puerto y para transferir el archivo A CLIENT A SERVER 4444 A CLIENT A CLIENT Después que el cliente contacta al servidor, éste crea otro proceso para para atender al cliente y se queda escuchando el puerto 4444 por otro A CLIENT A SERVER 4444 A CLIENT A CLIENT Mientras el nuevo proceso está atendiendo al primer cliente, el segundo cliente puede contactar al servidor en el puerto 4444 A CLIENT A SERVER 4444 A CLIENT A CLIENT Y el servidor crea otro proceso A CLIENT A SERVER 4444 A CLIENT A CLIENT Ahora un tercer cliente contacta al servidor A CLIENT A SERVER 4444 A CLIENT A CLIENT Y un tercer proceso esclavo o thread es creado A CLIENT A SERVER 4444 A CLIENT A CLIENT Algoritmo de Servidor Concurrente Programa principal o “master” del servidor 1. Crear un Socket de servidor En un ciclo infinito: 2. Aceptar requerimientos de clientes 3. Cuando llega una petición de un cliente crear un nuevo proceso “esclavo” que atienda paralelamente la petición (esto no debe bloquear la ejecución del programa master del servidor) 4. Volver a 2. Proceso esclavo: 1. Recibir los parámetros de la comunicación (socket o flujos de entrada y/o salida) 2. Atender al cliente (ej: leer el nombre del archivo, transmitir el archivo) 3. Retornar (desaparecer !) Proceso que crea un hilo para cada Cliente Conectado public class MiHilo extends Thread { BufferedReader flujoL; PrintWriter flujoE; public MiHilo(Socket s) { // obtención de los flujos de L/E } public void run() { // resolución del problema // manejando los flujos de L/E } } public class Servidor { public static void main(String args[]) { ServerSocket serv; serv = new ServerSocket(9999); while(true) { Socket s = serv.accept(); MiHilo hilo = new MiHilo(s); hilo.start(); } } } Proceso que crea un hilo para la lectura y un hilo para la escritura a través de un socket public class MiHiloL extends Thread { BufferedReader flujoL; public MiHilo(Socket s) { // obtención del flujo de L } public void run() { // resolución del problema // manejando el flujo de L } } public class MiHiloE extends Thread { PrintWriter flujoE; public MiHilo(Socket s) { // obtención del flujo de E } public void run() { // resolución del problema // manejando el flujo de E } } public class Proceso { public static void main(String args[]) { Socket s = new Socket(“localhost”, 9999) MiHiloL hiloL = new MiHiloL(s); MiHiloE hiloE = new MiHiloE(s); hiloL.start(); hiloE.start(); } } } Ejercicio 1: Servidor: Acepta conexiones en el puerto 9999. Recibe una cadena del cliente y la convierte a mayúsculas. Creará un hilo para cada cliente conectado. Cliente: Envía una cadena al servidor y muestra en pantalla la respuesta del servidor. Ejercicio 2: Implementar un “teléfono” Servidor: Acepta 2 clientes conectados en el puerto 8888. Crea dos hilos para manejar lo que cada cliente envía. Cada hilo leerá la información de un socket “origen” y lo enviará por un socket “destino”. Finalizan cuando los dos clientes envíen la palabra “FIN”. Cliente: Creará un hilo para la lectura de frases del teclado y envío a través del socket. Finalizará cuando se introduzca “FIN”. Creará un hilo para mostrar por pantalla la información que lea del socket. Finalizará cuando reciba “FIN”.