Distribución en Java Concurrencia y Distribución Programación Avanzada Posgrado en Ciencia e Ingenierı́a de la Computación, UNAM 1. Introducción TCP/IP es un conjunto de protocolos que permite a dos programas comunicarse sobre un canal de comunicación, Esto se logra mediante crear una conexión de datos entre los dos programas. Una vez establecida, la conexión es vista por los dos programas como un archivoal cual el emisor escribe y el receptor lee. Un canal de comunicación bidireccional se estable mediante permitir a los dos programas la lectura y escritura del archivo. El proceso se ilustra en la Figura 1, donde un programa “cliente” establece contacto con un programa “servidor”, ambos ejecutándose en dos computadoras diferentes. Mediante esta aproximación, es posible ennviar transacciones a ser procesadas en otras máquinas. Computadora Servidor Computadora Cliente Programa Servidor Programa Cliente Figura 1: Cliente y sevidor. La siguiente tabla resume las responsabilidades de un cliente y un servidor: 1 Cliente Servidor 2. 2.1. Una aplicación o applet cliente envı́a transacciones a una aplicación o applet servidor para su procesamiento. Una aplicación o applet servidor procesa las transacciones de parte de uno o más clientes. Conceptos y términos Nombres de máquina y localhost Cada máquina conetada a Internet sae reconoce por una dirección IP única. Esta es en realidad un número entero de 32 bits que se escribe en forma de cuatro octetos de la forma 192.62.183.86. Sin embargo, como esto es una forma muy poco amigable de nombrar computadoras, se utiliza en lugar de esto un nombre simbólico de dominio. Un ejemplo de nombre de dominio es hp.fciencias.unam.mx, y se lee como la máquina hp está en el dominio fciencias, que se encuentra en el dominio unam, del dominio mx. Los dominios de más alta jerarquı́a se registran por acuerdo internacional, los niveles más balos por organizaciones o por personas responsables de tal dominio. Utilizando esta estrategia cada computadora del mundo tiene un único nombre de dominio que puede ser relativamente fácil de recordar. Por otro lado, en muchos sistemas de cómputo, el nombre de la computadora local tiene el “alias” de localhost. De tal modo, si se desea comunicarse con otra aplicación o applet que se ejecuta en la misma computadora, entonces es posible utilizar el nombre localhost como el nombre de la máquina. Esto permite probar programas que utilizan la red sin necesidad de conectar la computadora a la red. Por supuesto, es necesario que el software de redes necesario debe estar instalado en la computadora para que esto funcione. 2.2. Puertos Como puede haber muchos programas ejecutándose en una computadora individual, se utiliza un número de puerto lógico como identificador único del programa con que el usuario desea comunicarse. Un número de puerto se representa por un número entero dentro del rango de 1 y 65536. Los números de puerto entre 1 y 1024 se reservan para programas especı́ficos, de modo que debe evitarse su uso. Por ejemplo, el conjunto de programas FTP utilizan los puertos 20 y 21 para comunicar clientes y servidores FTP. 2 Utilizar uno de estos puertos puede cancelar el uso del cliente FTP de la máquina local, ası́ como obtener resultados extraños cuando el programa se comunica con otras máquinas. Desafortunadamente, algunos de los puertos numerados fuera del rango 1 a 1024 también se utilizan por otros programas. 2.3. Cliente Un programa cliente es aquél que solicita al servidor realizar una tarea de por su parte. El cliente inicia la conversación con el servidor. Un cliente “ligero” realiza muy poca labor local, delegando la mayorı́a del trabajo al servidor, mientras que un cliente “pesado” hace la mayor parte del trabajo por sı́ mismo. 2.4. Servidor Un programa servidor es aquél que realiza tareas de parte de sus clientes. El servidor puede comunicarse con el cliente para retornar resultados de su solicitud. 2.5. Socket Un socket es un extremo o punto de conexiones para communicación entre dos programas. En esencia, el socket es la conexión entre dos programas desde el punto de vista del programador. La creación de un socket sucede en dos casos principalmente: Cuando un cliente se conecta a un socket como una conexión a un puerto dado en una máquina dada. Cuando un servidor se conecta a un socket que se conecta a un puerto dado en la máquina anfitrión (host machine). Esta conexión resulta incompleta, y se completará solamente cuando un cliente se conecte a tal puerto. 2.6. Resumen La Figure 2 muestra cómo dos computadoras beta y gamma ejecutan programas cliente (programas B y C) que se comunican con un programa servidor (programa A) ejecutándose en el servidor alfa. Estas computadoras se encuentran todas en el dominio fciencias.unam.mx. 3 beta.fciencias.unam.mx alfa.fciencias.unam.mx Programa B S Programa A Servidor S Cliente 99 gamma.fciencias.unam.mx S Socket 99 Puerto Programa C S Cliente Figura 2: Dos clientes y un servidor. Los dos programas cliente crean un socket local que conectan al puerto 99 en la máquina con el nombre de dominio alfa.fciencias.unam.mx. Esta computadora ejecuta un programa servidor (programa A) que está constantemente “escuchando” el puerto 99, en espera de conexiones. Algunos de los principales conceptos de distribución se resumen en la siguiente tabla: 4 Dirección IP Nombre de dominio TCP UDP IP Número de puerto Host 3. Actualmente, un IPv4 (IP versión 4) usa un valor entero de 32 bits para la dirección normalmente escrita como 4 bytes de la forma: 192.62.183.86. Nótese que el formato de una dirección IP se expandirá a IPv6, permitiendo direcciones de 128 bits, lo que permitirá un mayor número de computadoras conectadas a Internet. Nombre simbólico de una máquina o grupo de máquinas. Cada dueño de un dominio puede registrar otros nombres en ese dominio. Por ejemplo, la UNAM (unam.mx) puede elegir los nombres de sus máquinas y otros sub-dominios en su dominio. Cada dominio usualmente tiene un DNS (Domain Name Server) que mapea el nombre de cada máquina en su dominio a la dirección IP real. Un DNS es una computadora que es capaz de proveer una dirección IP para un nombre de dominio o solicitar a otro DNS que provea de esta información. Transport Control Protocol. Un protocolo que garantiza que cuando se establece y mantiene abierta una conexión, la máquina receptora recibirá los mensajes que se le envı́an. El mensaje se divide en paquetes, que recorren cualquier ruta a su destino. Sin embargo, el protocolo TCP asegura el re-ensamble de todos los paquetes individuales en el orden correcto para entregarlo al receptor. User Datagram Protocol. En este protocolo, el mensaje se divide en paquetes y se envı́an al destino, pero no hay ninguna garantı́a de que los paquetes llegarán en el order correcto, o al menos llegarán. Internet Protocol. Una colección de protocolos de los que TCP y UDP son miembros. Número en el rango de 1 a 65536. Ejemplos de números de puerto utilizados son: 20 y 21, usados por FTP, 23, para telnet, y 79, para finger. La máquina o computadora en la que el programa se ejecuta. La implementación Desarrolle dos programas, uno cliente y uno servidor, para implementar un chatline simple, utilizando una interfaz gráfica de usuario (GUI). Este programa permite a varias personas intercambiar mensajes atrvés de Internet. Cada usuario del chatline ejecuta un programa cliente que envı́a 5 mensajes a un programa servidor central, y este re-envı́a estos mensajes a todos los clientes. Este proceso se muestra en la Figura 3. Cliente 1 Cliente 2 Servidor El servidor re−envia a cada cliente cualquier mensaje que se le envie. Cliente n Los clientes mandan mensajes al servidor, deslegando los mensajes que el servidor les envia. Figura 3: Un chatline. Una ilustración de dos personas interactuando mediante el chatline se muestra en la Figure 4. 3.1. La clase Client El cliente del chatline se compone de las siguientes clases: Clase Application Client TextArea NetReader NetWriter TextField Transaction Resumen Crea y maneja los componentes de comunicación. Inicia al cliente. El área de texto visual. Lee información del servidor remoto. Escribe información en el servidor remoto. Componente visual. Objeto que responde a una instancia de un TextField. El diagrama de clases se muestra en la Figura 5. La interfaz gráfica de usuario se implementa mediante crear la clase Application como una subclase de la clase Frame, que contiene los objetos 6 Maquina mc0.local Maquina mc1.local mc0.local:2000(Mike) started Mike: Alguien por ahi? cori: Si, yo... mc1.local:2000(cori) started Mike: Alguien por ahi? cori: Si, yo... Alguien por ahi? Si, yo... Figura 4: Dos personas comunicándose mediate el puerto 2000. Client 1 1 1 1 Application 1 1 1 1 1 NetReader 1 1 NetWriter TextArea 1 1 Transaction Figura 5: Diagrama de clases del cliente. 7 1 1 1 1 1 TextField utilizados en la interfaz gráfica. De tal modo, el constructor de la clase Application debe establecer dos ventanas: Una ventana de salida, theOutput, intancia de la clase TextArea Una ventana de entrada, theInput, instance de la clase TextField La ventana de salida se utliza para desplegar las conversasiones que actualmente se llevan a cabo en el chatline, mientras que la ventana de entrada se usa para que el usuaro local pueda contribuir a la conversación. El ciclo principal de el proceso debe estar contenido en un método start(), que permite leer los mensajes que se reciben del servidor, y desplegarlos en la ventana de salida. Esto sólo puede realizarse si se ha establecido una conexión. Para ello, el nombre de la máquina, el puerto y el nombre del usuario debe proveerse como parámetros de la lı́nea de comando, utilizando un método llamado params(). Este método debe implementarse de modo que decodifique los parámetros de la lı́nea de comandos, y si encuentra alguna inconsistencia, debe proveer un valor por defecto apropiado. Otro método, setUpConnect(), debe implementarse para establecer la conexión con la máquina servidor,, a través de un socket. Finalmente, la clase Transaction implemlementa la acción en la ventana de entrada, implementando la interfaz ActionListener. Esto requiere la implementación del método actionPerformed(), que se ejecuta cada vez que el usuario escribe un mensaje y presiona “enter”. 3.2. La clase Server El servidor lee los mensajes de texto de los clientes y los reenvı́a a todos aquéllos que se encuentren actualmente conectados. El servidor del chatline se compone, entonces, de las siguientes clases: Clase NetReader NetWriter Person Server TChatManager TClientReader Resumen Lee información del servidor remoto. Escribe información en el servidor remoto. Crea un objecto activo que se comunica con el cliente. Inicia el servidor. Maneja la comunicación entre el cliente y el servidor. Crea un objeto activo para leer información del cliente y provee una interfaz para leer información. 8 En esencia, el servidor crea múltuiples instancias de la clase Person para manejar a los usuarios individuales. Una instancia de la clase TChatManager es un objecto activo que maneja el flujo de mensajes entre los usuarios individuales. El diagrama de clases de la Figura 6 muestra la organización del servidor. 1 TClientReader 1 1 * Person Server 1 1 1 1 * NetReader 1 1 TChatManager 1 NetWriter Figura 6: Diagrama de clases del servidor. La clase Server debe implementar el ciclo principal de ejecución del servidor chatline. En esencia, los usuarios hacen contacto con este servidor. Por cada contacto, un objecto activo debe ser creado para controlar la comunicación con cada usuario. Además, un proceso por separado debe crearse para “polear” cada usuario en turno para ver si han hecho contacto con el servidor para enviar un mensaje, si lo han hecho, y entonces distribuirlo a todos los usuarios. 3.3. La clase TChatManager La clase TChatManager debe implementarse para controlar las instancias de la clase Person. Cada instancia de la clase Person es un objeto activo que se mantiene mediante un arreglo theChatters, de tamaño MAXPERSONS. El paper de una instancia de la clase Person es comunicarse con cada usuario del chatline. La clase TChatManager debe contar con un método add() que añada un nuevo usuario a su colección de usuarios. Este debe ser un método sincronizado para prevenir la corrupción y/o mal uso de theChatters como 9 recurso conpartido. El arreglo theChatters debe permitir añadir y accesar a differentes threads ejecutándose concurrentemente. Nótese que, en caso de que ae haya alcanzado el número máximo de usuarios, un nuevo usuario simplemente no puede conectarse. Otro método de la clase TChatManager es remove(), que se encarga de remover a un usuario inactivo de la colección de usuarios. Varios otros métodos son necesarios para un funcionamiento adecuado: el método getMessage() debe retornar un mensaje desde un usuario; si el usuario no ha transmitido ningún mensaje, entonces debe retornar un valor null. Este método es sincronizado, ya que cada usuario de la colección de usuarios theChatters es un objeto activo que contı́nuamente monitorea la lı́nea de comunicación entre sı́ mismo y su cliente. Otro método getName() simplemente debe retornar el nombre del usuario. El métdodo putMessage() debe escribir un mensaje al usuario. El método sendToAll() envı́a un mensaje a todos los usuarios, incluyendo al emisor. Finalmente, el método run() debe implementar la parte activa del objeto, que es un ciclo contı́nuo, poleando cada usuario por turno para ver si ha enviado un mensaje de texto. Si ası́ es, entonces el mensaje de texto se re-envı́a a todos los usuarios. Si el nombre del usuario no se conoce, entonces el nombre que se envı́a como parte del mensaje de texto debe utilizarse como el nombre del usuario. Si el usuario ha salido del chatline, un mensaje EOF debe recibirse, en cuyo caso los demás usuarios reciben un mensaje informando que tal usuario ha salido de la sesión. 3.4. La clase Person La clase Person debe utilizarse para crear objetos activos que contı́nuamente monitorean la conexión de comunicación del cliente, buscando un nuevo mensaje de texto. Esto se implementa como una funcionalidad de una instancia de la clase TClientReader. En esencia, la clase Person debe proveer de una interfaz limpia a los objetos responsables de leer y escribir los canales de comunicación. Debe contar con métdos para manipular lı́neas de texto: getLine(), putLine(), setName() y getName(). 10 3.5. La clase TClientReader La clase TClientReader debe crear un objeto activo que continuamente monitoree la lı́nea de comunicación entre un usuario, esperando un mensaje de texto para enviar. De tal modo, este proceso no debe bloquearse, para lo que se utiliza un buffer unitario para almacenar el mensaje de texto recibido. Se checa el buffer por un mensaje, en lugar de por una comunicación. De tal modo, puede hacerse una solicitud no-bloqueable para ver si el buffer contiene un mensaje. Como la espera de un mensaje de texto en el canal de comunicaciones es una operación que si puede bloquearse, esta funcionalidad debe llevarse a cabo por la parte activa del objeto. Ası́, el método run() debe implementarse de modo que cuando se recibe un mensaje de texto, se le coloque en el buffer. Recuerde que las operaciones sobre el buffer deben ser sincronizadas, de modo que se prevenga la corrupción de datos. La clase debe contar con un método storeIfSpace(), que almacene el mensaje si hay espacio en el buffer. También, con un método getLine(), que retorna el mensaje de texto almacenado en el buffer. Si no hay mensajes, debe retornar un valor null. 3.6. La clase NetReader La clase NetReader debe simplificar laq lectura de datos desde el canal de comunicación que se obtiene de un socket. La cadena EOF se da como valor de retorno en el canal cuando un archivo termina o cuando se detecta un error. La razón común de un error es que el canal ha sido cerrado del otro lado, en cuyo caso retornar EOF resulta la acción apropiada. De tal modo, esta clase cuenta con un método getLine() el cual lee una lı́nea de caracteres del canal de comunicación, y cualquier error debe tratarse como un fin de archivo. También, debe contar con un método close() que cierre el canal de manera ordenada. 3.7. La clase NetWriter La clase NetWriter debe utilizarse para simplificar la escritura de datos a un canal que se obtiene mediante un socket. El constructor de esta clase toma como argumento al socket para crear un stream de salida. Requiere contener el método putLine(), que escribe al canal de comunicación y el método close(), que cierra en canal en la máquina remota. 11 Referencias [1] Ken Arnold and James Gosling. The Java Programming Language. Addison-Wesley, 1996. [2] Barry Boone. Java Essentials for C and C++ Programmers. AddisonWesley, 1996. [3] Distributed Processes: A Concurrent Programming Concept. In Communications of the ACM 21(11), 1978, pp. 934-941. [4] Gary Cornell and Cay S. Horstsmann. Core Java. Prentice-Hall, 1996. [5] David Flanagan. Java in a Nutshell. O’Reilly, 1996. 12