Sunteți pe pagina 1din 37

Curso 2004/2005

Modelo Cliente Servidor en Java

TEMA 1 MODELO DE CLIENTE SERVIDOR EN JAVA El binomio cliente/servidor es una de las frases ms utilizadas en nuestros das en el campo de la computacin, al menos tan usada como Java, aunque no tanto como Internet. El modelo ms moderno de programacin de redes se basa en el modelo de cliente servidor que se muestra en la Figura 1

Servidor

Cliente

Los clientes inician las conexiones a un puerto conocido del servidor siempre que este disponible un puerto del cliente Puerto 10 Puerto 412 Entrada a 10 Salida de 10 Entrada a 412

Salida de 412 El servidor acepta la conexin. Se conectan a los sockets de entrada y salida de formato libre sobre los puertos especificados
Figura 1 Modelo de cliente-servidor

Una aplicacin cliente-servidor por lo general almacena gran cantidad de datos sobre un servidor caro y altamente potente, mientras que la mayora de los programas lgicos, y de interfaz de usuario, se utilizan por el software cliente ejecutndose sobre un computador personal relativamente barato. En la mayora de los casos el servidor es el que bsicamente enva datos, mientras que el cliente bsicamente los recibe, pero es raro que un programa solo enve o solo reciba datos de forma exclusiva. La distincin ms clara, aunque no definitiva, es que un cliente inicia la conexin, mientras que un servidor espera a que los clientes inicien las conexiones con el. En algunos casos, un mismo programa puede ser a la vez cliente y servidor Algunos servidores analizan los datos antes de enviar los resultados al cliente, son las aplicaciones servidoras y se llaman as, para distinguirlas de los servidores ms comunes de ficheros y de bases de datos

Ampliacin de Sistemas Operativos

Csar Hervs

Curso 2004/2005

Modelo Cliente Servidor en Java

En 1999, el sistema ms popular de cliente-servidor es el World Wide Web. Servidores Web como Apache responden a peticiones desde clientes web como Netscape Navigator. Los datos son almacenados en el servidor de web y este entonces los enva a los clientes que los solicitan. Los servidores web que utilizan programas CGI, son servidores dobles de archivos y de aplicaciones. CGI es la puerta comn de la interfaz, se utiliza para generar pginas Web dinmicamente; el hojeador llama a un programa sobre el servidor que crea una nueva pgina. Esta pgina web puede estar basada nicamente sobre el dato servidor o puede procesar los resultados por entregar de un cliente. Se pueden escribir programas CGI en casi todos los lenguajes actuales de programacin incluyendo Java, aunque corrientemente la mayora de los programas estn hechos en Perl, C, o AppleScript Un servicio antiguo que utiliza el modelo de cliente servidor es FTP. FTP utiliza diferentes protocolos de aplicaciones y diferente software, pero estos protocolos estn divididos en servidores FTP que envan ficheros, y en clientes FTP que los reciben. Java hace fcil escribir servidores de todas clases, pero es realmente brillante cuando se emplea para escribir programas clientes. Java es un potente entorno en el cual se pueden escribir programas GUI que acceden a muchas clases de servidores. En 1997, el ejemplo por excelencia de un programa escrito en Java es HotJava, el web browser de Sun, el cual es un web cliente de propsito general.
Aplicaciones Igual a Igual Peer-to-Peer No todas las aplicaciones se sitan fcilmente dentro del modelo cliente-servidor. Por ejemplo, en los juegos en redes, ambos jugadores enviarn datos hacia atrs y hacia adelante de forma aproximadamente igual. Estas clases de conexiones se llaman peer-topeer. El sistema telefnico es el ejemplo clsico de este tipo de conexin en red. Cada telfono puede llamar a, o ser llamado por, otro telfono.

Java no tiene de forma explcita comunicacin peer-to-peer en su API en red. Sin embargo las aplicaciones pueden, de forma fcil, implementar comunicacin peer-to-peer de forma tal que acten tanto como clientes como servidores.
Sockets para clientes Los datos se transmiten a travs de Internet en paquetes de tamao finito llamados datagramas (datagrams). Cada datagrama contiene una cabecera (header) y una zona de carga (payload). La cabecera contiene la direccin y el puerto a donde va, la direccin y el puerto de donde viene, y otras diferentes informaciones usuales utilizadas para asegurar una transmisin confiable y segura. La zona de carga contiene los datos propiamente dichos. Sin embargo, dado que los datagramas tienen una longitud finita, es necesario cortar los datos a travs de mltiples paquetes y juntarlos en el destino.

Es posible adems que uno o varios paquetes se pierdan o se corrompan en la transmisin, y es necesario volverlos a transmitir, o que los paquetes lleguen en un orden diferente y es necesario ordenarlos. La realizacin de este procedimiento (partir los datos en paquetes, generar cabeceras, analizar las cabeceras de paquetes perdidos, analizar los paquetes

Ampliacin de Sistemas Operativos

Csar Hervs

Curso 2004/2005

Modelo Cliente Servidor en Java

recibidos y no recibidos, etc.) requiere de una gran cantidad de trabajo y un software especfico. Para dar respuesta a la realizacin del procedimiento anterior en el S.O. UNIX de Berkeley, BSD, se han implementado los Sockets. Permiten al programador tratar una conexin de red como cualquier cadena de bytes que pueden ser escritos en, o ledos desde, la red. Los sockets evitan al programador los detalles de bajo nivel de la red, como los tipos de medios de comunicacin, los tamaos de los paquetes, la transmisin de paquetes, las direcciones de red, etc. Un Socket puede realizar siete operaciones bsicas: 1. Conectarse a una mquina remota (esto es, prepararse para enviar o recibir datos) 2. Enviar datos 3. Recibir datos 4. Cerrar una conexin 5. Unirse a un puerto 6. Escuchar las peticiones de datos 7. Aceptar conexiones desde mquinas remotas sobre el puerto de unin La clase Socket de Java, utilizada tanto por clientes como por servidores, tiene mtodos que se corresponden con las cuatro primeras operaciones; mientras que las tres ltimas solo son necesarias para servidores que necesitan esperar a clientes que se conecten con ellos y por tanto son implementadas por la clase ServerSocket, que se ver ms adelante. Los sockets clientes se utilizaran normalmente de la forma:

Se crea el nuevo socket utilizando el constructor Socket(). El socket solicita conectarse a un host remoto. Una vez que la conexin est efectuada, los hosts remoto y local obtienen flujos de entrada y de salida desde los sockets, y utilizan esos flujos para enviar datos a cada uno de los otros. Esta conexin es bidireccional full-duplex, ambos hosts pueden enviar y recibir datos simultneamente. El significado de los datos depende del protocolo; se envan diferentes mandatos a un servidor FTP as como a un servidor HTTP. Cuando se haya completado la transmisin de datos, uno de los dos lados cierra la conexin. Algunos protocolos tales como HTTP, necesitan que la conexin se cierre despus de que sea servida cada peticin. Otros, como FTP, permiten mltiples conexiones para ser procesadas en una sola conexin.
Ampliacin de Sistemas Operativos Csar Hervs 3

Curso 2004/2005

Modelo Cliente Servidor en Java

Ejemplo de protocolos con telnet Para obtener un mayor conocimiento de como opera un protocolo, podemos utilizar el protocolo telnet para conectarnos a un servidor, utilizar diferentes tipos de mandatos en el, y esperar su respuesta. Por defecto, telnet intenta conectarse al puerto 23. Para conectarse a servidores en diferentes puertos, se especifica el puerto de la forma:

% telnet localhost 25 Este ejemplo pide una conexin al puerto 25, el puerto SMTP, sobre la mquina local; SMTP es el protocolo utilizado para transferir email entre servidores, o entre un mail cliente y uno servidor. Ahora, si se saben los mandatos para interactuar con un servidor SMTP, se puede enviar email sin hacerlo a travs de un programa mail. Este truco se puede utilizar para falsificar email, por lo que para dar crdito a email es necesario realizar una verificacin independiente.
Qu hay en la librera de redes? Si usted observa los fuentes de la librera de redes en $JAVAHOME/src/java/net, encontrar las siguientes clases, adems de alguna ms, pero estas son las ms importantes. InetAddress

Clase que representa las direcciones IP y las operaciones entre ellas. La clase deber haber sido llamada IP o Address. URL Clase que representa al localizador de recursos uniforme Uniform Resource Locator, una referencia a un objeto sobre el web. Puede crear una referencia URL con esta clase (una direccin) URLConnection Puede abrir una clase URL, y recuperar los contenidos, o escribirlos, usando esta clase. HttpURLConnection Clase que extiende URLConnection y soporta funciones especficas para HTTP, como get, post, put, head, trace, y otras opciones. URLEncoder/ Estas dos clases tienen mtodos estticos para permitir convertir URLDecoder una cadena a y desde la forma MIME x-www-form-urlencoded. Esto es conveniente para traspasar datos a scripts CGI Socket Esta es la clase Socket cliente y utiliza una clase SocketImpl para implantar las operaciones del socket actual. Esto le permite cambiar las implantaciones socket dependiendo de la clase de activacin que est utilizando. ServerSocket Esta es la clase Socket servidor. ServerSocket le permite a una aplicacin aceptar conexiones TCP. La clase DatagramSocket permite a un servidor aceptar paquetes UDP

Sockets y comunicacin inter-procesos. La construccin del lenguaje que ms se utiliza para todo lo concerniente a la comunicacin clienteservidor es el socket. Cada programa lee desde o escribe a un socket
Ampliacin de Sistemas Operativos Csar Hervs 4

Curso 2004/2005

Modelo Cliente Servidor en Java

de la misma manera que nosotros abrimos, leemos, escribimos a, y cerramos un fichero. En esencia existen dos tipos de sockets: 1. Un tipo que es similar a un telfono (una conexin orientada a servicio, esto es, Transmission Control Protocol (TCP) ) 2. Un tipo que es anlogo a un buzn (un servicio de conexiones mediante datagramas, esto es, Unreliable Datagram Protocol (UDP) ) Una diferencia importante entre los dos tipos de sockets es que el protocolo TCP aporta la seguridad de que todo lo que se enva se obtiene en la otra parte mientras que en el protocolo UDP no es as, de esta forma el emisor deber chequear que ha sido recibido por el receptor. La eleccin entre usar datagramas o un socket de conexin viene determinado fcilmente por la naturaleza de la conexin. Si todos los datos estn fijados en un datagrama de 8K y no necesita conocer si lo han recibido al otro lado entonces lo ideal es utilizar datagramas UDP. Si el tiempo de servicio es grande al establecer una conexin, o si es necesario que todos los paquetes se revisen en el mismo orden en el que fueron enviados, tal como transferir un fichero de mas de 8K de longitud, entonces se deber de utilizar un socket TCP. Las clases de Java del paquete java.net se muestran en la tabla Clase Socket (Cliente) ServerSocket (Servidor) DatagramSocket DatagramPacket Uniform Resource Locator (URL) URL Connection Descrpcin punto final TCP (un telfono) punto final TCP (un recepcionista) punto final UDP (un buzn) paquete UDP (una carta) (una direccin) Conexin a un objeto web (por ejemplo, un guin CGI-bin)

Ampliacin de Sistemas Operativos

Csar Hervs

Curso 2004/2005

Modelo Cliente Servidor en Java

LA CLASE SOCKET Los constructores Los cuatro constructores pblicos en Java 1.1 son sencillos. En cada uno se necesita especificar el host y el puerto al que nos queremos conectar. Los hosts se pueden especificar como una direccin de Internet (InetAdress) o una cadena (String). Los puertos se especifican siempre como un valor entero int comprendido entre 0 y 65535. Dos de los constructores especifican adems la direccin local y el puerto local desde el que se envan los datos. Necesitaremos especificar una direccin local cuando queramos seleccionar una interfaz particular de red o un host multihomed (algunos sistemas operativos como Solaris, permiten multihoming: una mquina puede responder a diferentes direcciones IP. Es posible entonces unir un servidor web diferente a cada direccin IP, y ejecutar diferentes sites que se ejecutan independientemente pero que comparten algn hardware. Esto es bastante comn a los servidores web de estancias).

En Java 1.1, la clase Socket tiene tambin dos constructores protegidos. Las clases socket cliente y servidor, probablemente nunca necesitarn usarlos; pero ser importante tenerlo en cuenta si se crea una subclase de Socket (quizs para implementar un nuevo tipo de Socket que realice automticamente encriptacin, autenticacin o compresin de datos)
public Socket(String host, int port) throws UnknownHostException, IOException Este constructor crea un socket TCP para el puerto especificado del host especificado, e intenta conectarse al host remoto. Por ejemplo: try { Socket_to_Ora = new Socket(www.ora.com, 80); } catch (UnknowHostException e) { System.err.println(e); } catch (IOException e) { System.err.println(e); }

En esta construccin, el argumento host es en realidad un nombre de host, expresado como una cadena, no un URL como http://www.ora.com, o un objeto InetAddress. Si el host es desconocido o el dominio del servidor de nombres no est en funcionamiento, el constructor devuelve un UnknowHostException. Si el socket no puede estar abierto por alguna otra razn, el constructor enva un IOException. Existen muchas razones por las que un intento de conexin puede fallar: el host al que intenta conectarse puede no aceptar conexiones, su conexin a Internet puede estar cada, o puede existir algn problema de enrutado que haga que los paquetes no encuentren su destino. Puesto que este constructor no crea un objeto Socket, sino que intenta conectar el socket a un host remoto, puede usarlo para determinar si estn permitidas las conexiones a un puerto particular, como se muestra en el ejemplo.
Ampliacin de Sistemas Operativos Csar Hervs 6

Curso 2004/2005

Modelo Cliente Servidor en Java

Ejemplo El software esta diseado para encontrar cuales de los primeros 1024 puertos parecen ser hosts de servidores TCP import java.net.*; import java.io.*; public class lookForPorts { public static void main(String[] args) { Socket theSocket; String host = localhost; if (args.length > 0) { host = args[0]; } for (int i = 0; i < 1024; i++) { try { theSocket = new Socket(host, i); System.out.println(Hay un servidor sobre el puerto + i + de host); } catch (UnknownHostException e) { System.err.println(e); break; } catch (IOException e) { // no debe haber un servidor sobre este puerto } }// end for }// end main } // end lookForPorts

La salida producida por la ejecucin del programa sobre un host local, indica que por regla general la mayora de los puertos estarn ocupando una estacin de trabajo UNIX en vez de PC o Macintosh % java lookForPorts Hay un servidor sobre el puerto 7 de localhost Hay un servidor sobre el puerto 9 de localhost Hay un servidor sobre el puerto 13 de localhost Hay un servidor sobre el puerto 19 de localhost Hay un servidor sobre el puerto 21 de localhost Hay un servidor sobre el puerto 23 de localhost Hay un servidor sobre el puerto 25 de localhost Hay un servidor sobre el puerto 77 de localhost Hay un servidor sobre el puerto 79 de localhost
Ampliacin de Sistemas Operativos Csar Hervs 7

Curso 2004/2005

Modelo Cliente Servidor en Java

Hay un servidor sobre el puerto 512 de localhost Hay un servidor sobre el puerto 513 de localhost % Si tenemos curiosidad por saber que servidores se estn ejecutando en estos puertos, se puede intentar ejecutando el mandato telnet. Sobre un sistema UNIX, se puede ser capaz de encontrar, que servicios residen en que puertos, mirando el fichero /etc/services. Si lookForPorts encuentra algn puerto que esta ejecutando servidores pero no est listado en /etc/services, es que este no es de inters. El programa aunque sencillo nos ayuda a entender que es lo que est haciendo el sistema de forma tal que podamos encontrar (y cerrar) posibles puntos de entrada para atacar al sistema, adems de proporcionarnos servidores especficos. No ejecute este programa en una mquina que no sea suya; la mayora de los administradores de sistemas lo consideraran como un acto hostil.
public Socket(InetAddress host, int port) throws IOException Como el constructor anterior, este constructor crea un socket TCP para el puerto especificado sobre el host especificado, e intenta la conexin. Difiere del anterior en el uso de un objeto InetAddress porque especifica la direccin del host, en vez del nombre_del_host. El socket manda una IOException si no puede conectarse, pero no manda un UnknownHostException; si el host es desconocido, lo encontrara cuando cree el objeto InetAddress.

Como habr muchos sockets al mismo host, es a veces ms eficiente convertir el nombre_del_host a un InetAddress, y entonces utilizar de forma repetida ese InetAddress para crear sockets. El siguiente ejemplo utiliza esta tcnica para mejorar la eficiencia del ejemplo anterior.
Ejemplo El siguiente ejemplo encuentra que puertos mayores o iguales de 1024 pueden ser hosts de servidores TCP.

import java.net.*; import java.io.*; public class lookForPorts2 { public static void main(String[] args) { Socket theSocket; String host = localhost; if (args.length > 0) { host = args[0]; } try { InetAddress theAddress = InetAddress.getByName(host); for (int i = 1024; i < 65536; i++) { try {
Ampliacin de Sistemas Operativos Csar Hervs 8

Curso 2004/2005

Modelo Cliente Servidor en Java

theSocket = new Socket(host, i); System.out.println(Hay un servidor sobre el puerto + i + de + host); } catch (IOException e) { // no debe haber un servidor sobre este puerto } }// end for }// end try catch (UnknownHostException e) { System.err.println(e); } }// end main } // end lookForPorts2 Los resultados son similares a los obtenidos anteriormente, a excepcin de que lookForPorts2 chequea los puertos por encima de 1024, y devuelve cualquier servidor que encuentre.
public Socket(String host, int port, InetAddress interface, int localPort) throws IOException Esta construccin crea un socket TCP al puerto especificado en el host especificado, e intenta conectarse. Se conecta al host y al puerto especificado en los dos primeros de sus argumentos. Se conecta desde la interfaz de red y el puerto local especificado en los dos ltimos argumentos. La interfaz de red puede ser fsica (por ejemplo una tarjeta Ethernet diferente) o virtual (un host con mltiples direcciones IP). Si se pasa un 0 como argumento de localPort, se elegir aleatoriamente un puerto, (tambin definido como anonymus port) entre 1024 y 65535. Por ejemplo, si estamos ejecutando un programa sobre calzone.oit.unc.edu, y queremos estar seguros de que la conexin va a 100 megabit-porsegundo (MBPS) en la interfaz mediante un anillo de FDDI (sunsite.unc.edu), en lugar de la interfaz a 10-MBPS utilizando Ethernet, abrimos un socket de la forma.

InetAddress sunsite; Socket ORASocket; try { sunsite = new InetAddress(sunsite.unc.edu); } catch (UnknownHostException e) { System.err.println(e); } try { ORASocket = new Socket (www.ora.com, 80, sunsite, 0); } catch (IOException e) { System.err.println(e) ; }

Ampliacin de Sistemas Operativos

Csar Hervs

Curso 2004/2005

Modelo Cliente Servidor en Java

Usando un 0 como nmero del puerto local, indicara que le da igual el puerto a utilizar para la interfaz de red utilizando FDDI. Este constructor puede mandar una IOException por las mismas razones dadas en los anteriores constructores. Adems, se puede presentar una IOException (de forma especifica si esta es UnknownHostException), aunque no est declarada en la clusula del constructor y ser presentada si el host no puede ser localizado. Por ltimo, una IOException (probablemente una BindException, aunque de nuevo, sta no est declarada en la clusula del constructor) ser presentada si el socket no est disponible para unirlo a la requerida interfaz de red. Esto tiende a limitar la portabilidad de las aplicaciones que utiliza este constructor. Este constructor puede ser ventajoso si se restringe la ejecucin de un programa compilado sobre un host predeterminado. Para ello es posible que necesitemos acomodar las distribuciones de procesamiento de cada computadora, lo que es ciertamente excesivo. Los programas en Java son fciles de desensamblar, descompilar y gestionar en sentido contrario ya que este esquema est lejos de no tener fallos. Sin embargo, esto puede ser parte de un esquema que refuerce una licencia de software, o que prevenga de empleos no permitidos del e-mailing del software de su propiedad a sus competidores
public Socket(InetAddress host, int port, InetAddress interface, int localPort) throws IOException Este constructor es idntico al anterior con la excepcin de que el host a conectarse se pasa como un InetAddress, no como un String. Crea por tanto un socket TCP al puerto especificado sobre el host especificado desde la interfaz y el puerto local especificados, e intenta conectarse. Si falla manda un IOException. Por ejemplo

InetAddress sunsite, ora; Socket ORASocket; try { sunsite = new InetAddress(sunsite.unc.edu); ora = new InetAddress(www.ora.com); } catch (UnknownHostException e) { System.err.println(e); } try { ORASocket = new Socket (ora, 80, sunsite, 0); } catch (IOException e) { System.err.println(e) ; }
proteccin de Socket()

Ampliacin de Sistemas Operativos

Csar Hervs

10

Curso 2004/2005

Modelo Cliente Servidor en Java

La clase Socket tambin tiene dos constructores de proteccin que inician la superclase sin conectar el socket. Puede utilizarlo, si esta en la subclase Socket, quizs para implementar una clase especial de Socket que encripte intercambios o entienda su servidor sustituto local. La mayora de las implantaciones de una nueva clase socket estarn escritas en un objeto SocketImpl. El constructor sin argumentos Socket(), instala por defecto SocketImpl (o desde fbrica o mediante un java.net.PlainSocketImpl). Crea un nuevo Socket sin conectarse a l, y es llamado por lo general por subclases de java.net.Socket.
protected Socket() Este constructor instala el objeto SocketImpl impl cuando crea un nuevo Socket objeto. El objeto Socket se crea pero no se conecta. Este constructor es por lo general llamado por las subclases de java.net.Socket. Ejemplo de programa cliente Cuando hablamos acerca de aplicaciones de comunicaciones estamos realmente hablando de puertos sobre cada final de un enlace de comunicaciones. Los puertos son similares a una lnea telefnica. El telfono enlazado a s mismo se puede ver como un puerto. Sobre una computadora, estos puertos estn disponibles sobre cualquier mquina en red basada en el protocolo TCP/IP. Un ejemplo de escritura de un programa en Java, donde implantamos un eco y donde llamamos al puerto 7 de TCP, que es el puerto estndar del comando echo public class EchoTest { DataInputStream in; EchoTest () { in = new DataInputStream(System.in); } public static void main(String[] args) { Socket theSocket; String args[]; } { String line; while(true) { line=; try { line = in.readline(); catch (IOException e) {
Ampliacin de Sistemas Operativos Csar Hervs 11

Curso 2004/2005

Modelo Cliente Servidor en Java

} } // end EchoTest

System.err.println(e.getMessage()); } System.out.println(line);

El cdigo muestra como, en primer lugar, se crea una clase EchoTest que contiene una variable, Stream, con la cual nos conectaremos a nuestro socket. A continuacin, inicializamos el dato Stream dentro de nuestro constructor. Se le suministra la entrada estndar del sistema (el teclado) como la variable que especifica la fuente de entrada Stream. Se completa el programa creando un bucle infinito mediante el cual obtenemos una lnea sobre la pantalla inmediatamente despus de haberla escrito por el teclado.
Introduccin de sockets en Java Para optimizar la eficiencia de la utilizacin de sockets en Java modificamos el programa anterior de construccin de un eco, siguiendo los pasos:

1. 2. 3. 4.

Leemos una lnea desde el teclado La escribimos a un socket conectado al puerto 7 de TCP Leemos la rplica desde la conexin del socket Imprimimos la lnea desde el socket a la pantalla

Creamos la clase Socket (clase cliente) en la forma


Sockets = Socket(*localhost, 7);

Donde los dos argumentos del constructor Socket son el nombre del host y el nmero del puerto. El nombre del host se pasa como una variable String desde la lnea de mandatos. Aparte de las llamadas al sistema operativo, observamos que el protocolo de Socket no est especificado, puesto que los sockets de Java son siempre sockets TCP. Primero, debemos de crear una clase EchoClient e importar todas las libreras de Java que utilizarn nuestro programa.
import java.io.*; import java.net.*; public class EchoClient{

A continuacin deberemos de crear una funcin en la que exista un bucle similar al que creamos con nuestro cliente en Java. Este bucle deber de tener dos objetos sobre los que poder actuar, el DataInputStream del socket desde el que se obtienen datos, y el DataOutput Stream del socket al cual el EchoClient le escribir los datos. Supondremos que esta ser la entrada y la salida estndar para nuestro cliente en Java, pero no haremos esta suposicin por ahora.
public static void echoclient(
Ampliacin de Sistemas Operativos Csar Hervs 12

Curso 2004/2005

Modelo Cliente Servidor en Java

DataInputStream sin, DataOutputStream sout ) throws IOException { DataInputStream in = new DataInputStream (System.in); PrintStream out = new PrintStream(sout); String line; while(true) { line=; // lee del teclado y escribe en el socket TCP try { line = in.readline(); out.println( line ); } catch (IOException e) { System.err.println(e.getMessage()); } try { // Ahora leemos del socket TCP y escribimos en el terminal line = sin.readLine(); System.out.println(line);

} catch (IOException e) {

System.err.println(e.getMessage()); } } } // end EchoTest

Por ltimo vamos a crear el programa principal que cree el primer socket, obtenga un DataInputStream y un DataOutputStream basado en l. Esto nos permitir leer de, y escribir en, el socket, as como, pasarle la informacin a la funcin que hemos creado con anterioridad. Una vez finalizado, deberemos de cerrar la conexin al socket, puesto que si no lo hacemos es posible que otras aplicaciones no sean capaces de conectarse.
public static void main( String[] args ) { Socket s = null;
Ampliacin de Sistemas Operativos Csar Hervs 13

Curso 2004/2005

Modelo Cliente Servidor en Java

try { // Crea un socket para comunicarse con echo // sobre el host especificado s = new Socket(arg[0], 7); // Crea streams para leer y escribir lneas // de texto desde, y a, este socket. DataInputStream sin = new DataInputStream (s.getInputStream()); DataOutputStream sout = new DataOutputStream (s.getOutputStream()); //Llamar al usuario que hemos conectado System.out.println(Connected to + s.getInetAddres() + : + s.getPort()); echoclient (sin, sout); } catch (IOException e) { System.err.println(e); } // Siempre deberemos de asegurarnos de cerrar el socket finally { try {

if(s != null) { s.close(); } catch (IOException exc) } }

Cuando se ejecuta el programa enviamos un mensaje al socket echo, y entonces leemos esta informacin que regresa al socket y la imprimimos. Si se necesita conectarse a otro host, se sustituye su nombre por el localhost.
% prompt% java EchoClient localhost Connected to local host/150.0.0.1:7
Ampliacin de Sistemas Operativos Csar Hervs 14

Curso 2004/2005

Modelo Cliente Servidor en Java

abc abc xyz xyz C

request.... ...reply request.... ...reply

Este servicio se puede testar con el comando telnet que esta disponible sobre todo computadora en red. En este caso el programa telnet acta de la misma manera que nuestro cliente, enviando informacin al puerto y leyendo todo lo que devuelva.
% prompt% telnet localhost 7 Trying 150.0.0.1... Connected to localhost. Escape character is ]. abc request.... abc ...reply C xyz request.... xyz ...reply ] control-right-bracket telnet> quit Connection closed

Ampliacin de Sistemas Operativos

Csar Hervs

15

Curso 2004/2005

Modelo Cliente Servidor en Java

SOCKETS PARA SERVIDORES Servidores TCP Un servicio orientado a conexin es mejor para aplicaciones que necesitan que la informacin, mediante caracteres, sea recibida en el mismo orden en el que fue enviada, tal como los caracteres tecleados desde un terminal, o la transferencia de bytes en un fichero ASCII. Habitualmente, la conexin permanece abierta durante un tiempo relacionado con la cantidad de tiempo necesaria para definir la conexin (un handsake de tres paquetes IP).

Los protocolos orientados a conexin, tales como TCP, envan un reconocimiento cuando se recibe el dato, y entonces retransmiten el dato automticamente si no se recibe un reconocimiento antes de que haya terminado un periodo de tiempo. Cada paquete de reconocimiento enva al lado receptor cuanto espacio de buffer esta disponible en el otro final de la conexin. Esto habilita a ambos puntos finales para transmitir una ventana de datos, quizs varios paquetes de 8K, antes de parar para esperar a un reconocimiento desde el otro final. Cuando se recibe el reconocimiento, el tamao de ventana es pasado desde la cabecera del paquete. Esto habilita a TCP para acelerar la transferencia de datos cuando un lado esta ejecutndose lentamente sobre un espacio del buffer y para incrementar la transferencia de datos cuando el otro lado tiene mucho espacio para recibir datos.
Metodologa del servidor Una aplicacin tpica de TCP abre un puerto bien conocido para recibir peticiones de conexin, y entonces generar un proceso hijo o un hilo separado de ejecucin para realizar el servicio pedido. Esto asegura que el servidor est siempre preparado para escuchar ms peticiones. Un simple servidor multihilo debe interrogar constantemente a los sockets, y cuando detecta actividad debe generar un nuevo proceso para manejar la peticin de entrada. El servidor multihilos puede sencillamente esperar que llegue informacin a un socket y expandir un hilo para manejar las peticiones de entrada. Qu es un socket servidor? Java proporciona una clase ServerSocket para permitir escribir servidores. Bsicamente, el trabajo de un socket servidor consiste en situarse con un telfono y esperar las llamadas de entrada. De forma ms tcnica, la clase ServerSocket se ejecuta sobre el servidor, y espera a conexiones de entrada. Cada ServerSocket espera sobre un puerto particular en el host local. Cuando un socket cliente, sobre un host remoto, intenta conectarse a ese puerto, el servidor despierta, negocia la conexin entre el cliente y el servidor, y abre un Socket regular entre los dos hosts. En otras palabras, los sockets servidores esperan por conexiones mientras que los sockets clientes inician las conexiones. Una vez que el servidor socket tiene definida la conexin, el servidor utiliza un Socket regular para enviar datos al cliente. Los datos siempre viajan sobre el Socket regular.

La clase ServerSocket contiene todo lo que se necesita para escribir servidores en Java. Tiene constructores que crean nuevos objetos ServerSockets, mtodos que escuchan para
Ampliacin de Sistemas Operativos Csar Hervs 16

Curso 2004/2005

Modelo Cliente Servidor en Java

conexiones sobre un puerto especfico, y mtodos que devuelven un Socket cuando se acepta una conexin, de forma tal que se puedan enviar y recibir datos. Adems tiene los mtodos habituales de miscelnea, como toString(). El ciclo de vida bsico de un servidor es: 1. Se crea un nuevo ServerSocket sobre un puerto particular utilizando el constructor ServerSocket(). 2. El ServerSocket escucha los intentos de conexiones entrantes sobre ese puerto utilizando el mtodo accept(). Accept() se bloquea hasta que un cliente intenta hacer una conexin, en cuyo punto accept() devuelve un objeto Socket que conecta el cliente al servidor. 3. Dependiendo del tipo de servidor se utiliza, o el mtodo getInputStream() de Socket, o el mtodo getOutputStream(), o ambos, para obtener flujos de entrada y de salida que comuniquen con el cliente. 4. El servidor y el cliente intercomunican de acuerdo a un protocolo acordado, hasta que sea el tiempo de cerrar la conexin. 5. El servidor, el cliente o ambos, cierran la conexin. 6. El servidor vuelve a la etapa 2, y espera a la siguiente conexin. Si la etapa 4 probablemente tiene una longitud o un incremento indefinido de tiempo, los servidores tradicionales de UNIX como wu-ftpd crean un nuevo proceso para manejar cada conexin. En Java, se puede expandir un hilo que comunique con el cliente, de forma tal que el servidor pueda estar preparado para procesar con prontitud la siguiente conexin. Los hilos producen unas cargas ms pequeas sobre el servidor que un proceso hijo completo. En efecto, el incremento al expandir demasiados procesos, hace que el servidor tpico FTP de UNIX no pueda manejar nada mas que aproximadamente 400 conexiones sin que se haga intil. Por otra parte, si el protocolo es sencillo, rpido, y permite al servidor cerrar la conexin cuando se haya realizado la transmisin, ser ms eficiente, para el servidor, procesar la peticin del cliente inmediatamente sin crear un hilo. El sistema operativo almacena las direcciones de las conexiones de entrada de clientes a un puerto particular en una cola FIFO. La longitud, por defecto, de la cola es normalmente 50, aunque esto puede variar de un SO a otro. Algn sistema operativo (aunque no es el caso de Solaris) tiene una longitud mxima de cola de 5. Sobre estos sistemas, la longitud de la cola ser el mayor valor posible que sea menor o igual que 50. Despus de que la cola alcance su capacidad con las conexiones no procesadas, el host rechaza peticiones adicionales, sobre ese puerto, hasta que las ranuras de la cola se abran. La mayora, (aunque no todos), de los clientes intentaran hacer una conexin mltiples veces, si su peticin inicial es rechazada. El manejo de las conexiones de entrada, y de la cola, es un servicio proporcionado por el sistema operativo; su programa no necesita preocuparse de ello. Varios constructores ServerSocket le permiten cambiar la longitud de la cola si la
Ampliacin de Sistemas Operativos Csar Hervs 17

Curso 2004/2005

Modelo Cliente Servidor en Java

longitud por defecto no es bastante grande; sin embargo, usted no esta capacitado para aumentar la cola ms all de un tamao mximo determinado por su SO.
LOS CONSTRUCTORES public ServerSocket(int port) throws IOException, BindException Este constructor crea un socket servidor para el puerto especficado. Si usted define mediante 0 el nombre del puerto, el sistema selecciona para usted un puerto disponible, a este puerto se le llama a veces anonymus port puesto que usted no sabe su nmero. Para los servidores los puertos annimos no son muy tiles porque los clientes necesitan conocer con anterioridad con que puerto conectarse; sin embargo existen unas pocas situaciones (que se discutirn ms adelante) en las que puede ser til un puerto annimo. Ejemplo Para crear un socket servidor que pueda utilizarse por un servidor HTTP sobre el puerto 80 escribiremos try { ServerSocket httpd = new ServerSocket (80);

} catch (IOException e) { System.err.println(e); }

El constructor manda un IOException si no puede crear y unir el socket al puerto pedido. (En Java 1.1 se enva en su lugar un BindException, el cual se hereda desde IOException)). Una IOException cuando se crea un ServerSocket significa una de las siguientes cosas: o el puerto especificado est ya en uso, o no tiene privilegios de root en UNIX y esta intentando conectarse a un puerto entre 1 y 1023. Puede utilizarse este constructor para escribir una variante del programa lookForPorts, de la siguiente manera.
import java.net.*; import java.io.*; public class lookForLocalPorts { public static void main(String[ ] args) { ServerSocket theServer; for (int i = 1024; i < 65535; i++) { try { // las siguientes lneas nos indicaran fallo y cada, fail and drop, dentro del bloque obtenido, si ya existe un servidor ejecutndose sobre el puerto i //
Ampliacin de Sistemas Operativos Csar Hervs 18

Curso 2004/2005

Modelo Cliente Servidor en Java

theServer = new.ServerSocket (i); theServer.close(); } catch (IOException e) { System.out.println(Hay un servidor sobre un puerto + i + .); }// end try }// end for }// end main } // end lookForLocalPorts

El ejemplo chequea puertos sobre la mquina local para intentar crear objetos ServerSocket sobre ellos, y ver en que puertos falla el intento. Si usted esta utilizando UNIX y no es root (superusuario), el programa slo trabaja para puertos 1024 y siguientes.
public ServerSocket(int port, int queuelength) throws IOException, BindException Este constructor crea un ServerSocket sobre el puerto especificado con una longitud de cola que usted elige. El argumento queuelength define la longitud de la cola para las conexiones entrantes, esto es, cuantas conexiones entrantes pueden almacenarse en un instante, antes de que el host empiece a rechazar conexiones. Si intenta expandir la cola ms all de la mxima longitud de la cola, se utilizar en su lugar la mxima longitud de la cola. Si utiliza un 0 para especificar el nmero del puerto, el sistema seleccionar un puerto disponible.

Por ejemplo, para crear un socket servidor sobre el puerto 5776 que pueda contener 100 conexiones de entrada en la cola escribiremos.
try { ServerSocket httpd = new ServerSocket (5776, 100);

} catch (IOException e) { System.err.println(e); }

El constructor manda una IOException si el socket no puede ser creado y unido sobre el puerto requerido. (En Java 1.1 se enva en su lugar un BindException, el cual hereda desde IOException)). Esto casi siempre significa una de las dos cosas siguientes: o el puerto especificado ya est en uso, o no tiene privilegios de superusuario en UNIX y esta intentando conectarse a un puerto entre 1 y 1023.
public ServerSocket(int port, int queuelength, InetAddress bind Address) throws IOException Este constructor crea un ServerSocket sobre el puerto especificado con una longitud de cola especificada. El ServerSocket solo enlaza sobre la direccin IP local especificada. Este constructor es til para servidores que se ejecutan sobre sistemas con varias direcciones IP (una prctica comn en los servidores web de estancias) porque le permite elegir la
Ampliacin de Sistemas Operativos Csar Hervs 19

Curso 2004/2005

Modelo Cliente Servidor en Java

direccin en la que estar escuchando. Esto es, ServerSocket slo escucha a conexiones de entrada sobre la direccin especificada; esto no escuchara a conexiones que vengan mandadas a otras direcciones del host. Los otros constructores enlazan por defecto con toda direccin IP local. Por ejemplo sunsite.unc.edu es una estacin de trabajo particular de Carolina del Norte. Esta conectada a Internet mediante una interfaz FDDI extremadamente rpida a 1000Mbps (MBPS) con la direccin IP 152.2.254.81. La misma estacin de trabajo se llama calzone.oit.unc.edu, pero calzone est conectada a Internet mediante una tarjeta Ethernet a 10 MBPS, utilizando una direccin IP diferente (152.2.22.81). Para crear un socket servidor que escuche sobre el puerto 5776 de sunsite pero no por el puerto 5776 de calzone deberemos escribir
try { ServerSocket httpd = new ServerSocket (5776, 100); InetAddress.getHostByName(sunsite.unc.edu));

} catch (IOException e) { System.err.println(e); }

El constructor manda una IOException si el socket no puede ser creado y unido sobre el puerto requerido por idnticos motivos vistos en los anteriores constructores.
Proteccin de ServerSocket() El constructor de proteccin protected es para la utilizacin de subclases de ServerSocket que queramos prevenir de su propio SocketImpl, quizs para pasar hacia un servidor alternativo o implementar protocolos de seguridad. El constructor crea un SocketImpl por defecto, impl, en la superclase, pero no inicia impl. Esto es, l no llamar a impl.create(), impl.bind() o impl.listen(). De esta manera puede utilizar un objeto SocketImpl diferente, de su propiedad, para hacer todo el trabajo.

La subclase ServerSocket debe tambin supeditar accept(). El SocketImpl impl en la superclase no est accesible a otras de sus subclases. El mtodo de proteccin final void implAccept(Sockets) se utiliza en su lugar. Cuando implAccept() retorna, se conecta el socket. Este mtodo puede mandar una IOException.
Ejemplo de Aceptar y Cerrar conexiones Import java.net.*; import java.io.*; import java.util.Date; public class daytimeServer { public final static int daytimePort = 13; public static void main(String[ ] args) { ServerSocket theServer;
Ampliacin de Sistemas Operativos Csar Hervs 20

Curso 2004/2005

Modelo Cliente Servidor en Java

Socket theConnection; PrintStream p; try { theServer = new ServerSocket (daytimePort); try { while (true) { theConnection = theServer.accept(); p = new PrintStream (theConnection.getOutputStream()); p.println(new Date()); theConnection.close(); } } catch (IOException e) { theServer.close(); System.err.println(e); } }// end try catch (IOException e) { System.err.println(e); }

Las primeras tres lneas importan los paquetes usuales, java.io y java.net, as como java.util.Date. Hay un campo sencillo public final static int (esto es, una constante) en la clase daytimePort, el cual define el puerto bien-conocido well-known del servidor daytime (puerto 13). La clase tiene un mtodo sencillo, main(), que hace todo el trabajo. El mtodo main() tiene tres variables locales: el ServerSocket theServer, el Socket theConnection, y un PrintStream p. Despus de que se declaren las variables, un bloque intentar-coger try-catch atrapa cualquier IOException que pueda ocurrir mientras se construye el ServerSocket theServer sobre el puerto daytime. Entonces, otro bloque trata de buscar excepciones mandadas mientras las conexiones eran aceptadas y llama a processed.accept() mediante un bucle infinito para escuchar nuevas conexiones; como muchos servidores, este programa nunca termina, sino que continua escuchando hasta que se manda una excepcin o se para manualmente. Cuando un cliente realiza una conexin, accept() devuelve un Socket, el cual se sita en theConnection, y el programa continua. Llamamos a getOutputStream() para obtener el flujo de salida asociado con ese Socket, y entonces encadenamos esa salida a un nuevo PrintStream p. Para obtener el dato actual, construimos un nuevo objeto Date, y lo enviamos al cliente para imprimirlo sobre el PrintStream con println(). Despus de enviar el dato, llamamos al mtodo close() de theConnection. Siempre cerramos un socket cuando se ha terminado con l. Un cliente no deber contar con el otro lado de la conexin para cerrar el socket, por al menos tres razones: Los clientes pueden
Ampliacin de Sistemas Operativos Csar Hervs 21

Curso 2004/2005

Modelo Cliente Servidor en Java

esperar un tiempo time-out y terminar; los usuarios pueden cancelar intercomunicaciones; las redes pueden venirse abajo en periodos de alta densidad de trfico. Por cualquiera de estas u otras razones, no se puede contar con los clientes para cerrar un socket.
Ejemplo servidor de pizzas El PizzaServer que implementaremos estar unido al puerto 8205 y esperar una llamada de un cliente. Cuando el cliente enva su informacin delimitada por barras, el servidor expandir un hilo para manejar la peticin. El hilo lee la informacin, la procesa, y enva una respuesta. Definicin del servidor Deberemos crear el objeto PizzaServer por si mismo. El PizzaServer es una aplicacin de Java que permanece quieta con su propia aplicacin principal. Debemos tambin crear un PizzaThread que hereda de la clase Thread de Java. El objeto hilo ser creado cada vez que se detecte actividad en el puerto, y es una de las formas de implantar el objeto servidor. import import import import java.net.*; java.io.*; java.lang.*; java.util.*;

public class PizzaServer { public static void main( String args[] ) // Dentro del programa principal deberemos de crear un ServerSocket. El ServerSocket es // un socket tipo de Java cuyo nico propsito es estar disponible para esperar sobre un // socket a que exista actividad. El socket se inicializa especificando el puerto sobre el cual //se quieren esperar las peticiones. { // Inicializando la conexin a red try { Serversocket serversocket = new Serversocket (8205);

//Ahora creamos un hilo y esperamos a que ocurra alguna actividad. Una vez que //detectemos algn atisbo de informacin que viene del socket deberemos dejarle al hilo //obtener y procesar la informacin. Nuestro programa principal delega actividades en los //otros hilos. Para ello nos situamos en un bucle infinito hasta que obtengamos alguna //seal while(true) { //Aceptamos el mensaje Socket incoming = Serversocket.accept();
Ampliacin de Sistemas Operativos Csar Hervs 22

Curso 2004/2005

Modelo Cliente Servidor en Java

//Expande un hilo para manejar la peticin PizzaThread pt = new PizzaThread(incoming); pt.start(); } } catch (Exception exc) { System.out.println(Error! + exc.toString()); } } }

// hilos de pizza class PizzaThread extends Thread { //El objeto PizzaThread aceptar una variable, el socket entrante desde el cual obtenemos //informacin. Se necesita especificar aqu porque el programa servidor principal tiene ya //grabado el dominio del socket y no queremos hacerlo dos veces. Slo pasamos el socket //obtenido por el programa principal sobre el hilo. Tambin implantaremos el mtodo run //para el hilo Socket incoming; PizzaThread(Socket incoming) { this.incoming = incoming } // mtodo de ejecutar implantado por la clase hilo public void run() { try } //Una vez que el hilo esta ejecutando, necesita ir al socket y obtener informacin desde l. //Para hacer esto, debemos obtener dentro y fuera Streams del socket. Si recordamos el //socket es tan solo un constructor. En orden a obtener informacin, deberemos abstraernos //dentro de un mecanismo de entrada/salida. Deberemos de ser capaces de leer y escribir en //el socket. Como vimos al estudiar la clase cliente, los datos que estn dispuestos para //recibirlos en un formato delimitado por | . Deberemos utilizar el objeto StringTokenizer //para obtener la informacin desde el mensaje. // Obtenemos entrada desde el socket DataInputStream in = new DataInputStream(incoming.getInputStream()); // Obtenemos salida al socket
Ampliacin de Sistemas Operativos Csar Hervs 23

Curso 2004/2005

Modelo Cliente Servidor en Java

PrintStream out = new PrintStream(incoming.getOutputStream()); //Ahora obtenemos entradas desde el servidor hasta que el cierre la conexin boolean finished = false; while (finished) { String newOrder = in.readLine(); //convertirlo a un formato legible try { String Tokenizer stk = new StringTokenizer(newOrder, |); String name =stk.nextRToken(); String address = stk.nextToken(); String phone = stk.nextToken(); int size = Integer.valueOf(stk.nextToken()).intValue(); int toppings = Integer.valueOf(stk.nextToken()).intValue(); //no se hace ninguna excepcin para obtener el calculo total int total = (size * 5) + (toppings * 1); //se enva el resultado de regreso al cliente out.println($ + total + .00); //se sita el resultado en la pantalla System.out,println( pizza for + name + fue + totalString); } catch(NoSuchElementException exc) { finished = true; } } } catch (Exception exc) { System.outprintln(Error! + exc.toString()); } //Cerrar la conexin try {
Ampliacin de Sistemas Operativos Csar Hervs 24

Curso 2004/2005

Modelo Cliente Servidor en Java

incoming.close(); } catch (Exception exc) { System.out.println(Error! + exc.toString()); }

Las lneas que utilizamos para leer informacin desde el socket y enviar la informacin hacia atrs.
String newOrder = in.readLine(); ... out.println($ + total + .00);

tienen la misma sintaxis como si hubieran sido ledas y escritas en un fichero


Ejemplo de servidor controlador de clientes El programa clientTester que se ejecuta sobre un puerto especificado en la lnea de mandatos, muestra todos los datos enviados por el cliente, y le permite enviar una respuesta al cliente mediante su salida sobre la lnea de mandatos. Por ejemplo, se puede utilizar el programa para ver los mandatos Netscape Navigator enviados a un servidor.

El programa servidor utiliza dos hilos; uno para manejar entradas del cliente, y el otro para enviar salidas desde el servidor. Utilizando dos hilos se permite al programa manejar simultneamente entrada y salida; de esta forma puede enviar una respuesta al cliente mientras que recibe una peticin, o lo que es ms importante en este caso, puede enviar datos al cliente mientras espera a la respuesta del cliente. Esto es muy interesante puesto que diferentes clientes y servidores comunican en formas y maneras impredecibles. Con algunos protocolos, el servidor comunica primero, con otros, el cliente comunica primero. A veces, el servidor enva una breve respuesta one-line; a menudo, la respuesta es mucho mayor. Nuestro programa deber ser bastante flexible para manejar todos estos casos. Una vez que se ha establecido la conexin, el socket resultante se divide en flujo de entrada, y flujo de salida, cada uno con su propio hilo. Las peticiones de los clientes se transfieren a la pantalla un byte cada instante. Debido a que la entrada por lnea de mandato en Java es lnea a lnea, en el programa se utiliza un PrintStream para enviar respuestas de vuelta a los clientes.
import java.net.*; import java.io.*; public class clientTester { public static void main(String[ ] args) { int thePort;
Ampliacin de Sistemas Operativos Csar Hervs 25

Curso 2004/2005

Modelo Cliente Servidor en Java

ServerSocket ss; Socket theConnection; try { thePort = Integer.parseInt (args[0]); } catch (Exception e) { thePort = 0 } try { ss = new ServerSocket (thePort); System.out.println(Escuchando conexiones sobre el puerto + ss.getLocalPort ()); while (true) { theConnection = ss.accept (); System.out.println(Conexin establecida con + theConnection); InputThread it = new InputThread(the Connection.get.InputStream ()); It.start (); OutputThread ot = new OutputThread(the Connection.get.OutputStream () , it); ot.start (); // necesita esperar a los hilos ot e it para finalizar try { ot.join (); it.join (); } catch (InterruptedException e) { } } } catch (IOException e) { } }

class InputThread extends Thread { InputStream is; public InputThread(InputStream is) this.is = is; } public void run() { try { while (true) { int i = is.read();
Ampliacin de Sistemas Operativos Csar Hervs 26

Curso 2004/2005

Modelo Cliente Servidor en Java

if (i == -1) break; char c = (char) i; System.out.print (c)

catch (IOException e) { System.err.println(e); }

class OutputThread extends Thread { PrintStream ps; DataInputStream is; InputThread it; public OutputThread(OutputStream os, InputThread it) { ps = new PrintStream(os); this.it = it; is = new DataInputStream(System.in); } public void run() { String line; try { while (true) { line = is.readLine(); if (line.equals(.)) break; ps.println(line); } catch (IOException e) { } it.stop();

Comentario. La aplicacin clientTaster se divide en tres clases: clientTeste, InputThread y OutputThread. La clase clientTester lee el puerto escrito sobre la lnea de mandatos, abre una clase servidora ServerSocket, ss, sobre ese puerto, y escucha a conexiones de entrada. Podemos hacer una conexin cada vez, porque el programa se ha desarrollado para experimentacin, de forma tal que se nos proporcionen todas las respuestas del servidor. Las conexiones posteriores sern rechazadas hasta que no se cierre la primera.

Ampliacin de Sistemas Operativos

Csar Hervs

27

Curso 2004/2005

Modelo Cliente Servidor en Java

Un bucle infinito while espera a conexiones realizadas con el mtodo accept(). Cuando se detecta una conexin, su InputStream se utiliza para construir un nuevo InputThread, y su OutputStream se utiliza para construir un nuevo OutPutThread. Despus de iniciar estos hilos, los esperaremos para finalizar llamando a sus mtodos conjuntos join(). El InputThread esta contenido casi por completo en el mtodo run(). Tiene un solo campo, is, el InputStream, dato que ser ledo a una velocidad de un byte en cada instante. Cada byte se emite a un char, c, que se imprime sobre System.out. El mtodo run() termina cuando se enva una IOException, presumiblemente porque hemos encontrado el final del flujo. El OutputThread enva la salida al cliente. Su constructor tiene dos argumentos, un flujo de salida para enviar datos al cliente, y el InputThread. El hilo OutputThread lee la entrada desde el usuario sobre System.in, el cual est encadenado a DataInputStream is. El OutputStream que ser pasado al constructor lo encadena a un PrintStream por conveniencia. El mtodo run() para OutputThread lee las lneas desde el DataInputStream is, y las copia a continuacin sobre PrintStream ps, el cual las enva al cliente. Si tecleamos un punto sobre la lnea de mandatos, seala, por si mismo, el final de la entrada del usuario. Cuando ocurre esto, run() saldr del bucle, y parar el InputThread llamando a it.stop().
Ejemplo de servidor http El siguiente ejemplo muestra un servidor que siempre enva el mismo fichero, no importa quin o que le pidan; el nombre del fichero y el puerto local son ledos desde la lnea de mandatos. Si se omite el nmero de puerto, se supone que es el puerto 80 import java.net.*; import java.io.*; import java.util.*; public class onefile extends Thread { static String theData; static String ContentType; static int ContentLength; Socket theConnection; public static void main(String[ ] args) { int thePort; ServerSocket ss; Socket theConnection; FileInputStream theFile; //reserva el fichero try { theFile = new FileInputStream(args[0]); DataInputStream dis = new DataInputStream(theFile); If (args[0].endsWith(.html)) || args[0].endsWith(.htm)) {
Ampliacin de Sistemas Operativos Csar Hervs 28

Curso 2004/2005

Modelo Cliente Servidor en Java

} else { ContentType = text/plain; } try {

ContentType = text/html;

String thisLine; while ((thisLine = dis.readLine()) != null) { theData += thisLine + \n; } catch (IOException e) { System.err.println(Error: + e); }

} catch (IOException e) { System.err.println(e); System.err.println(Uso: java fichero nombre puerto); System.exit(1); } //define el puerto donde escuchar try { thePort = Integer.parseInt (args[1]); if (thePort < 0 || thePort > 65535) thePort = 80; } catch (Exception e) { thePort = 80 } try { ss = new ServerSocket (thePort); System.out.println(Aceptando conexiones sobre el puerto + ss.getLocalPort ()); System.out.println(Datos para ser enviados: ); System.out.println(theData ); while (true) { onefile fs = new onefile(ss.accept()); fs.start() } } catch (IOException e) { } } public onefile(Socket s) { theConnection = s; }

Ampliacin de Sistemas Operativos

Csar Hervs

29

Curso 2004/2005

Modelo Cliente Servidor en Java

public void run() { PrintStream os = new PrintStream (theConnection.getOutputStream()); DataInputStream is = new DataInputStream(theConnection.getInputStream()); String request = is.readLine(); // If this is HTTP/1.0 o later send a MIME header if (request.indexOf(HTTP/) != -1) { while(true) {// read the rest of the MIME header String thisLine = is.readLine(); If (thisLine.trim().equals()) break; } os.print(HTTP/1.0 200 OK\r\n); Date now = new Date(); os.print(Date: + now + \r\n); os.print(Server: OneFile 1.0\r\n); os.print(Content-length: + ContentLength + \r\n); os.print(Content-type: + ContentType + \r\n\r\n); }// end if os.println(theDate); theConnection.close(); }// end try catch (IOException e) { } } } Comentario Los servidores generan un nuevo hilo para cada peticin de entrada. Debido a que el servidor es muy sencillo, se puede analizar si la utilizacin de hilos separados ayuda o estorba al rendimiento. En gran medida depender del tamao del fichero que est siendo servido, del nmero de conexiones esperadas por hora, y del modelo de hilo de Java sobre la mquina. Cuando exista duda, la generacin de hilos es por lo general una buena idea y es la que se utilizara en esta solucin. La utilizacin de mltiples hilos ser una clara ganancia de un servidor que sea ligeramente ms sofisticado que el actual. try {

El mtodo main de la clase onefile manejara todas las tareas de inicializacin, entrara a continuacin un bucle while que aceptar conexiones y expande hilos para procesar cada conexin. El nombre del fichero a ser servido se lee del primer argumento introducido en la lnea de mandatos. Si no se especifica ningn fichero o el fichero no se puede abrir, se imprime un mensaje de error y termina el programa. Suponiendo que el fichero puede ser ledo, sus contenidos se almacenan en la variable esttica theData. Se realiza una suposicin razonable acerca del tipo de contenido del fichero, y esa suposicin se almacena en la variable esttica ContentType. Para mantener la eficiencia, el fichero se sita en memoria (la variable TheData) cuando se inicia el servidor; esto es ms rpido que leer el fichero desde disco para cada conexin. Si
Ampliacin de Sistemas Operativos Csar Hervs 30

Curso 2004/2005

Modelo Cliente Servidor en Java

el fichero es demasiado grande esto requerira una fraccin significativa de memoria disponible, no debera enviarlo sobre la web. Sin embargo, capturar el fichero significa que usted no puede cambiar el fichero sin restaurar el servidor. Si esto es un problema, se deber sencillamente extender este servidor para escuchar por conexiones sobre un puerto adicional de administracin, y releer el fichero cuando se detecta una conexin. Un servidor ms avanzado podr utilizar esta tcnica para cambiar muchos ms aspectos de su configuracin. Onefile lee el puerto por donde escuchar del segundo argumento de la lnea de mandatos. Si no se especifica un puerto, o si el segundo argumento no es un nmero entero comprendido entre 0 y 65535, se utilizar el puerto 80. Despus de completada la inicializacin, onefile abre un ServerSocket sobre el puerto especificado e imprime un mensaje mostrando el puerto que est siendo utilizado, y que datos se enviaran a los clientes. A continuacin, el programa entra en un bucle infinito que acepta conexiones de forma continuada, y que expande hilos para procesarlas. Se expande un hilo para construir una nueva instancia de la clase onefile con ss.accept() como un argumento. Se inicializa a continuacin el hilo, transfiriendo control al mtodo run del hilo. El mtodo run del hilo crea un PrintStream para la salida al socket, y un DataInputStream para entrada del socket. De esta forma lee las peticiones del cliente. Mira a la primera lnea para ver si contiene la cadena http/. Si la ve, el servidor supone que el cliente entiende http 1.0 o posterior, y de esta forma enva una cabecera MIME para el fichero; a continuacin enva los datos. Si la peticin del cliente no contiene la cadena http/, nuestro servidor omite la cabecera, enviando tan slo los datos. Por ltimo el servidor cierra la conexin, y elimina el hilo. A continuacin mostramos lo que se puede ver cuando nos conectamos a este servidor va telnet; las especificidades dependen del servidor exacto y del fichero
& telnet sunsite.unc.edu 2000 Trying 152.2.254.81 Conneccted to sunsite.unc.edu. Escape character is Ejercicio 1
Analice el cdigo, muestre como ejecutarlo en la lnea de mandatos y la respuesta obtenida por el sistema. import java.net.*; import java.io.*; import java.util.*; public class Redirector extends Thread { static String theSite; Socket Conexion; public Redirector(Socket s) { Conexion = s; } public static void main(String[ ] args) {

int thePort;

Ampliacin de Sistemas Operativos

Csar Hervs

31

Curso 2004/2005

Modelo Cliente Servidor en Java

ServerSocket ss; try { theSite = args[0]; catch (IOException e) { theSite = http://www.ora.com; } //trim trailing slash if (theSite.endsWith(/)) theSite = TheSite.substring(0, theSite.length()-1); } try { the Port = Integer.parseInt(arg[1]); } catch (Exception e) { thePort = 80 } try { ss = new ServerSocket (thePort); System.out.println(Redireccionando conexiones sobre el puerto + ss.getLocalPort () + a + theSite); while (true) { Redirector rd = new Redirector(ss.accept()); rd.start() } } catch (IOException e) { } }// end main public void run() { try { PrintStream os = new PrintStream (Conexion.getOutputStream()); DataInputStream is = new DataInputStream(Conexion.getInputStream()); String get = is.readLine(); StringTokenizer st = new StringTokenizer(get); st.nextToken(); String theFile = st.nextToken(); .............. // Cdigo para que el cliente entienda la versin HTTP/1.0 o superior .............. //Cdigo para enviar la cabecera HTTP 1.0) ............. //Cdigo para que como no todos los web browser soportan redireccionamiento //se necesita producir HTML que diga donde se ha movido el documento ............ Conexion.close(); }
Ampliacin de Sistemas Operativos Csar Hervs 32

Curso 2004/2005

Modelo Cliente Servidor en Java

catch (IOException e) { } }// end run } Comentario


El cdigo desarrolla un servidor que redirecciona a los usuarios desde un lugar web a otro, en nuestro caso desde ora.com a www.ora.com . El ejemplo lee un URL y un nmero de puerto desde la lnea de mandatos, y redirecciona todas las peticiones que recibe al lugar indicado por el nuevo URL, utilizando un cdigo 302 FOUND. Para empezar el redirector sobre el puerto 2000, y redirigir las peticiones de entrada a http://www.ora.com, deberemos de teclear: % java redirector http://www.ora.com 2000 Redirecting connections on port 2000 to http://www.ora.com Si nos conectamos a este servidor mediante telnet, la salida del sistema ser % telnet sunsite.unc.edu 2000 Trying 152.2.254.81... Connected to sunsite.unc.edu Escape character is ] GET / HTTP/1.0 ....... El servidor es suficientemente grande para que presente una ventaja utilizar multihilos, lo que se implementa con la clase Thread. El mtodo main() acepta conexiones y expande nuevos hilos mientras que el mtodo run() procesa cada conexin. La inicializacin es sencilla: main() lee el URL que conexiones son redireccionadas a y el puerto sobre el cual escuchar desde la lnea de mandatos. Si hay algn error, debido a que un argumento de la lnea de mandatos se omite o por alguna otra razn, Redirector escucha sobre el puerto 80, y redirecciona las peticiones a http://www.ora.com A continuacin Redirector abre un ServerSocket sobre el puerto especificado. Suponiendo que se realiza con xito, main() entra en un bucle infinito while para accept() conexiones de entrada y expande nuevos hilos para entenderse con cada peticin. Dentro de este bucle, main() llama al constructor para Redirector. Este toma un objeto Socket (devuelto por el accept()) como su argumento, y entonces llama al run() del Redirector para empezar el hilo. El mtodo run realiza la mayora del trabajo. Empieza por crear un PrimtStream os desde el strem de salida del Socket, y un DataInputStream is desde el strem de entrada del Socket. Entonces el mtodo run()= espera a que el cliente empiece a hablar. Por ltimo se cierra la conexin y muere el hilo

Ejercicio 2 Analice el siguiente cdigo especificando cual es su misin. Explique el significado del socket pblico que aparece en el cdigo
import java.net.*; import java.io.*; public class daytimeClient { public static void main(String[] args) { Socket theSocket; String hostname
Ampliacin de Sistemas Operativos Csar Hervs 33

Curso 2004/2005

Modelo Cliente Servidor en Java

DataInputStream theTimeStream; if (args.length > 0) hostname= args[0]; } else { hostname = localhost; } try { theSocket = new Socket(hostname, 13); theTimeStream = new DataInputStream(theSocket.getInputStream()); String theTime = theTimeStream.readLine(); System.out.println(It is + theTime + at + hostname); }// end try catch (UnknownHostException e) { System.err.println(e); } catch (IOException e) { System.err.println(e); } }// end main } // end daytimeClient {

Comentario El constructor no crea un objeto Socket, sino que intenta conectar el socket a un host remoto, puede usarlo para determinar si las conexiones a un puerto particular estn permitidas, como se muestra en el ejemplo.

El cdigo esta diseado para encontrar la hora de un determinado host, sobre el puerto 13 que es sobre el que se sirve la escucha a servidores TCP del host La salida producida por la ejecucin del programa sobre un host local es la siguiente % java lucano It is 12:10 at lucano La clase pblica utilizada es la siguiente
public Socket(String host, int port) throws UnknownHostException, IOException

Este constructor crea un socket TCP para el puerto especfico del host especfico, e intenta conectarse al host remoto.
theSocket = new Socket(hostname, 13); theTimeStream = new DataInputStream(theSocket.getInputStream()); String theTime = theTimeStream.readLine(); System.out.println(It is + theTime + at + hostname); }// end try catch (UnknownHostException e) {
Ampliacin de Sistemas Operativos Csar Hervs 34

Curso 2004/2005

Modelo Cliente Servidor en Java

System.err.println(e); } catch (IOException e) { System.err.println(e); }

En esta construccin, el argumento host es en realidad un nombre de host, expresado como una cadena. Si el host es desconocido o el dominio del servidor de nombres no est en funcionamiento, el constructor devuelve un UnknowHostException. Si el socket no puede estar abierto por alguna otra razn, el constructor enva un IOException. Existen muchas razones por las que un intento de conexin puede fallar: el host al que intenta conectarse puede no aceptar conexiones, su conexin a Internet puede estar cada, o puede existir algn problema de enrutado puede hacer que los paquetes no encuentren su destino.
Ejercicio Analice el siguiente cdigo y responda a las siguientes preguntas, justificando las respuestas. a) Qu modelo de comunicacin distribuida presenta? Cuales son las principales caractersticas del modelo, analizndolas en el cdigo? b) Qu misin tienen, el, o los, sockets que aparecen? c) La clase Redirector De qu tipo es? Qu caractersticas especficas tiene? d) Analice el cdigo, muestre como ejecutarlo en la lnea de mandatos y la respuesta obtenida por el sistema. import java.net.*; import java.io.*; import java.util.*; public class Redirector extends Thread { static String theSite; Socket Conexion; public Redirector(Socket s) { Conexion = s; } public static void main(String[ ] args) { int thePort; ServerSocket ss; try { theSite = args[0]; catch (IOException e) { theSite = http://www.ora.com; } //trim trailing slash if (theSite.endsWith(/)) theSite = TheSite.substring(0, theSite.length()-1); } try { the Port = Integer.parseInt(arg[1]); } catch (Exception e) { thePort = 80 } try {
Ampliacin de Sistemas Operativos Csar Hervs 35

Curso 2004/2005

Modelo Cliente Servidor en Java

ss = new ServerSocket (thePort); System.out.println(Redireccionando conexiones sobre el puerto + ss.getLocalPort () + a + theSite); while (true) { Redirector rd = new Redirector(ss.accept()); rd.start() } } catch (IOException e) { } }// end main public void run() { try { PrintStream os = new PrintStream (Conexion.getOutputStream()); DataInputStream is = new DataInputStream(Conexion.getInputStream()); String get = is.readLine(); StringTokenizer st = new StringTokenizer(get); st.nextToken(); String theFile = st.nextToken(); .............. // Cdigo para que el cliente entienda la versin HTTP/1.0 o superior .............. //Cdigo para enviar la cabecera HTTP 1.0) ............. //Cdigo para que como no todos los web browser soportan redireccionamiento //se necesita producir HTML que diga donde se ha movido el documento ............ Conexion.close(); } catch (IOException e) { } }// end run } El cdigo desarrolla un servidor que redirecciona a los usuarios desde un lugar web a otro, en nuestro caso desde ora.com a www.ora.com . El ejemplo lee un URL y un nmero de puerto desde la lnea de mandatos, y redirecciona todas las peticiones que recibe al lugar indicado por el nuevo URL, utilizando un cdigo 302 FOUND. Para empezar el redirector sobre el puerto 2000, y redirigir las peticiones de entrada a http://www.ora.com, deberemos de teclear: % java redirector http://www.ora.com 2000 Redirecting connections on port 2000 to http://www.ora.com Si nos conectamos a este servidor mediante telnet, la salida del sistema ser % telnet sunsite.unc.edu 2000 Trying 152.2.254.81... Connected to sunsite.unc.edu Escape character is ] GET / HTTP/1.0 .......

Ampliacin de Sistemas Operativos

Csar Hervs

36

Curso 2004/2005

Modelo Cliente Servidor en Java

El servidor es suficientemente grande para que presente una ventaja utilizar multihilos, lo que se implementa con la clase Thread. El mtodo main() acepta conexiones y expande nuevos hilos mientras que el mtodo run() procesa cada conexin. La inicializacin es sencilla: main() lee el URL que conexiones son redireccionadas a y el puerto sobre el cual escuchar desde la lnea de mandatos. Si hay algn error, debido a que un argumento de la lnea de mandatos se omite o por alguna otra razn, Redirector escucha sobre el puerto 80, y redirecciona las peticiones a http://www.ora.com A continuacin Redirector abre un ServerSocket sobre el puerto especificado. Suponiendo que se realiza con xito, main() entra en un bucle infinito while para accept() conexiones de entrada y expande nuevos hilos para entenderse con cada peticin. Dentro de este bucle, main() llama al constructor para Redirector. Este toma un objeto Socket (devuelto por el accept()) como su argumento, y entonces llama al run() del Redirector para empezar el hilo. El mtodo run realiza la mayora del trabajo. Empieza por crear un PrimtStream os desde el strem de salida del Socket, y un DataInputStream is desde el strem de entrada del Socket. Entonces el mtodo run()= espera a que el cliente empiece a hablar. ... .Por ltimo se cierra la conexin y muere el hilo

Ampliacin de Sistemas Operativos

Csar Hervs

37

S-ar putea să vă placă și