El ejemplo generado en esta secci�n est� comprendido por dos aplicaciones: un cliente y un servidor. El servidor recibe continuamente paquetes de datagramas a trav�s de un socket datagramas. Cada paquete recibido por el servidor indica una petici�n del cliente de una cita famosa. Cuando el servidor recibe un datagrama, le responde enviando un datagrama que contiene un texto de s�lo una l�nea que contiene "la cita del momento" al cliente.
La aplicaci�n cliente de este ejemplo es muy sencilla -- env�a un datagrama al servidor que indica que le gustar�a recibir una cita del momento. Entonces el cliente espera a que el servidor le env�e un datagrama en respuesta.
Dos clases implementan la aplicaci�n servidor: QuoteServer y QuoteServerThread. Una s�la clase implementa la aplicaci�n cliente: QuoteClient.
Investiguemos estas clases empezando con la clase que contiene el m�todo main() de la aplicaci�n servidor.
Contiene una versi�n de applet de la clase QuoteClient.
�La Clase QuoteServer
La clase QuoteServer contiene un s�lo m�todo: el m�todo main() para la aplicaci�n servidor de citas. El m�todo main() s�lo crean un nuevo objeto QuoteServerThread y lo arranca.
class QuoteServer {
public static void main(String[] args) {
new QuoteServerThread().start();
}
}
El objeto QuoteServerThread implementa la l�gica principal del servidor de citas.
�La Clase QuoteServerThread
La clase QuoteServerThread es un Thread que se ejecuta cont�nuamente esperando peticiones a trav�s del un socket de datagramas.
QuoteServerThread tiene dos variables de ejemplar privadas. La primera, llamada socket, es una referencia a un objeto DatagramSocket object. Esta variable se inicializa a null. La segunda, qfs, es un objeto DataInputStream que se ha abierto sobre un fichero de texto ASCII que contiene una lista de citas. Cada vez que se llegue al servidor una petici�n de cita, el servidor recupera la sigueinte l�nea desde el stream de entrada.
Cuando el programa principal crea el QuoteServerThread que utiliza el �nico constructor disponible.
QuoteServerThread() {
super("QuoteServer");
try {
socket = new DatagramSocket();
System.out.println("QuoteServer listening on port: " + socket.getLocalPort());
} catch (java.net.SocketException e) {
System.err.println("Could not create datagram socket.");
}
this.openInputFile();
}
La primera l�nea de este cosntructor llama al constructor de la superclase (Thread) para inicializar el thread con el nombre "QuoteServer". La siguiente secci�n de c�digo es la parte cr�tica del constructor de QuoteServerThread -- crea un DatagramSocket. El QuoteServerThread utiliza este DatagramSocket para escuchar y responder las peticiones de citas de los clientes.
El socket es creado utilizando el constructor de DatagramSocket que no requiere arguementos.
socket = new DatagramSocket();
Una vez creado usando este constructor, el nuevo DatagramSocket se asigna a alg�n puerto local disponible. La clase DatagramSocket tiene otro constructor que permite especificar el puerto que quiere utilizar para asignarle el nuevo objeto DatagramSocket. Deber�as observar que ciertos puertos est�s dedicados a servicios "bien-conocidos" y que no pueden ser utilizados. Si se especifica un puerto que est� siendo utilizado, fallar� la creaci�n del DatagramSocket.
Despu�s de crear con �xito el DatagramSocket, el QuoteServerThread muestra un mensaje indicando el puerto al que se ha asignado el DatagramSocket. El QuoteClient necesita este n�mero de puerto para construir los paquetes de datagramas destinados a este puerto. Por eso, se debe utilizar este n�mero de puerto cuando ejecute el QuoteClient.
La �ltima l�nea del constructor de QuoteServerThread llama a un m�todo privado, openInputFile(), dentro de QuoteServerThread para abrir un fichero llamado one-liners.txt que contiene una lista de citas. Cada cita del fichero debe ser un l�nea en s� misma.
Ahora la parte interesante de QuoteServerThread -- es el m�todo run(). (El m�todo run() sobreescribe el m�todo run() de la clase Thread y proporciona la implementaci�n del thread. Para informaci�n sobre los Threads, puedea ver Threads de Control.
El m�todo run() QuoteServerThread primero comprueba que se ha creado un objeto DatagramSocket v�lido durante su construcci�n. Si socket es null, entonces el QuoteServerThread no podr�a desviar el DatagramSocket. Sin el socket, el servidor no puede operar, y el m�todo run() retorna.
De otra forma, el m�todo run() entra en un bucle infinito. Este bucle espera continuamente las peticiones de los clientes y responde estas peticiones. Este bucle contiene dos secciones cr�ticas de c�digo: la secci�n que escucha las peticiones y la que las responde, primero veremos la secci�n que recibe la peticiones.
packet = new DatagramPacket(buf, 256); socket.receive(packet); address = packet.getAddress(); port = packet.getPort();
La primera l�nea de c�digo crea un nuevo objeto DatagramPacket encargado de recibir un datagrama a trav�s del socket. Se puede decir que el nuevo DatagramPacket est� encargado de recibir datos desde el socket debido al constructor utilizado para crearlo. Este constructor requiere s�lo dos argumentos, un array de bytes que contiene los datos espec�ficos del cliente, y la longitud de este array. Cuando se construye un DatagramPacket para enviarlo a trav�s de un DatagramSocket, tambi�n debe suministrar la direcci�n de internet y el puerto de destino del paquete. Ver�s esto m�s adelante cuando expliquemos c�mo responde un servidor a las peticiones del cliente.
La segunda l�nea de c�digo recibe un datagrama desde el socket. La informaci�n contenida dentro del mensaje del datagrama se copia en el paquete creado en la l�nea anterior. El m�todo receive() se bloquea hasta que se reciba un paquete. Si no se recibe ning�n paquete, el servidor no hace ning�n progreso y simplemente espera.
Las dos l�neas siguientes obtienen la direcci�n de internet y el n�mero de puerto desde el que se ha recibido el datagrama. La direcci�n Internet y el n�mero de puerto indicado de donde vino el paquete. Este es donde el servidor debe responder. En este ejemplo, el array de bytes del datagrama no contiene informaci�n relevante. S�lo la llegada del paquete indica una petici�n por parte del cliente que puede ser encontrado en la direcci�n de Internet y el n�mero de puertos indicados en el datagrama.
En este punto, el servidor ha recibido un petici�n de una cita desde un cliente. Ahora el servidor debe responder. Las seis l�neas de c�digo siguientes construyen la respuesta y la envian.
if (qfs == null)
dString = new Date().toString();
else
dString = getNextQuote();
dString.getBytes(0, dString.length(), buf, 0);
packet = new DatagramPacket(buf, buf.length, address, port);
socket.send(packet);
Si el fichero de citas no se puede abrir por alguna raz�n, qfs es null. En este caso, el servidor de citas sirve la hora del d�a en su lugar. De otra forma, el servidor de citas obtiene la siguiente cita del fichero abierto. La l�nea de c�digo de la sentencia if convierte la cadena en un array de bytes.
La tercera l�nea de c�digo crea un nuevo objeto DatagramPacket utilizado para enviar el mensaje a trav�s del socket del datagrama. Se puede decir que el nuevo DatagramPacket est� destinado a enviar los datos a trav�s del socket porque el constructor lo utiliza para eso. Este cosntructor requiere cuatro argumentos. El primer argumento es el mismo que el utilizado por el constructor utilizado para crear los datagramas receptores: un array de bytes que contiene el mensaje del emisor al receptor y la longitud de este array. Los dos siguientes argumentos son diferentes: una direcci�n de Internet y un n�mero de puerto. Estos dos argumentos son la direcci�n completa del destino del datagrama y debe ser suministrada por el emisor del datagrama.
La cuarta l�nea de c�digo env�a el DatagramPacket de esta forma.
El �ltimo m�todo de inter�s de QuoteServerThread es el m�todo finalize(). Este m�todo hace la limpieza cuando el QuoteServerThread recoge la basura cerrando el DatagramSocket. Los puertos son recursos limitados y los sockets asignados a un puerto deben cerrarse cuando no se utilizan.
�La Clase QuoteClient
La clase QuoteClient implementa una aplicaci�n cliente para el QuoteServer. Esta aplicaci�n s�lo env�a una petici�n al QuoteServer, espera una respuesta, y cuando �sta se recibe la muestra en la salida estandard. Echemos un vistazo al c�digo.
La clase QuoteClient contiene un m�todo -- el m�todo main() para la aplicaci�n cliente. La parte superior de main() declara varias variables locales para su utilizaci�n.
int port; InetAddress address; DatagramSocket socket = null; DatagramPacket packet; byte[] sendBuf = new byte[256];
La siguiente secci�n procesa los argumentos de la l�nea de comandos utilizados para invocar la aplicaci�n QuoteClient.
if (args.length != 2) {
System.out.println("Usage: java DatagramClient <hostname> <port#>");
return;
}
Esta aplicaci�n requiere dos argumentos: el nombre de la m�quina en la que se est� ejecutando QuoteServer, y el n�mero de puerto por que el QuoteServer est� escuchando. Cuando arranca el QuoteServer muestra un n�mero de puerto. Este es el n�mero de puerto que debe utilizar en la l�nea de comandos cuando arranque QuoteClient.
Luego, el m�todo main() contiene un bloque try que contiene la l�gica principal del programa cliente. Este bloque try contiene tres secciones principales: una secci�n que crea un DatagramSocket, una secci�n que env�a una petici�n al servidor, y una secci�n que obtiene la respuesta del servidor.
Primero veremos el c�digo que crea un DatagramSocket.
socket = new DatagramSocket();
El cliente utiliza el mismo constructor para crear un DatagramSocket que el servidor. El DatagramSocket es asignado a cualquier puerto disponible.
Luego, el programa QuoteClient env�a una petici�n al servidor.
address = InetAddress.getByName(args[0]);
port = Integer.parseInt(args[1]);
packet = new DatagramPacket(sendBuf, 256, address, port);
socket.send(packet);
System.out.println("Client sent request packet.");
La primera l�nea de c�digo obtiene la direcci�n Internet del host nombrado en la l�nea de comandos. La segunda l�nea de c�digo obtiene el n�mero de puerto de la l�nea de comandos. Estas dos piezas de informaci�n son utilizadas para crear un DatagramPacket destinado a esa direcci�n de Internet y ese n�mero de puerto. La direcci�n Internet y el n�mero de puertos deber�an indicar la m�quina en la que se arranc� el servidor y el puerto por el que el servidor est� escuchando.
La tercera l�nea del c�digo anterior crea un DatagramPacket utilizado para env�ar datos. El paquete est� construido con un array de bytes vac�os, su longitud, y la direcci�n Internet y el n�mero de puerto de destino del paquete. El array de bytes est� vac�o porque este datagrama s�lo pide la informaci�n del servidor. Todo lo que el servidor necesita saber para responder -- la direcci�n y el n�mero de puerto donde responder -- es una parte autom�tica del paquete.
Luego, el cliente obtiene una respuesta desde el servidor.
packet = new DatagramPacket(sendBuf, 256);
socket.receive(packet);
String received = new String(packet.getData(), 0);
System.out.println("Client received packet: " + received);
Para obtener una respuesta del servidor, el cliente crea un paquete receptor y utiliza el m�todo receive() del DatagramSocket para recibir la respuesta del servidor. El m�todo receive() se bloquea hasta que un datagrama destinado al cliente entre a trav�s del socket. Observa que si, por alguna raz�n, se pierde la respuesta del servidor, el cliente quedar� bloqueado d�bido a la pol�tica de no garant�as del modelo de datagrama. Normalmente, un cliente selecciona un tiempo para no est�r esperando eternamente una respuesta -- si la respuesta no llega, el temporizador se cumple, y el servidor retransmite la petici�n.
Cuando el cliente recibe una respuesta del servidor, utiliza el m�todo getData() para recuperar los datos del paquete. El cliente convierte los datos en una cadena y los muestra.
�Ejecutar el Servidor
Despu�s de haber compilado con �xito los programas cliente y servidor, puedes ejecutarlos. Primero debes ejecutar el servidor porque necesitas conocer el n�mero de puerto que muestra antes de poder arrancar el cliente. Cuando el servidor asigna con �xito su DatagramSocket, muestra un mensaje similar a este.
QuoteServer listening on port: portNumber
portNumber es el n�mero de puerto al que est� asignado el DatagramSocket. Utiliza este n�mero para arrancar el cliente.
�Ejecutar el Cliente
Una vez que has arrancado el servidor y mostrado el mensaje que indica el puerto en el que est� escuchando, puedes ejecutar el programa cliente. Recuerda ejecutar el programa cliente con dos argumentos en la l�nea de comandos: el nombre del host en el se est� ejecutando el QuoteServer, y el n�mero de puerto que mostr� al arrancar.
Despu�s de que el cliente env�e una petici�n y reciba una respuesta desde el servidor, deber�as ver una salida similar a �sta.
Quote of the Moment: Life is wonderful. Without it we'd all be dead.