Esta secci�n muestra c�mo escribir el lado del servidor de una conexi�n socket, con un ejemplo completo cliente-servidor. El servidor en el pareja cliente/servidor sirve bromas "Knock Knock". Las bromas Knock Knock son las favoritas por los ni�os peque�os y que normalmente son veh�culos para malos juegos de palabras. Son de esta forma.
Servidor: "Knock knock!"
Cliente: "�Qui�n es?"
Servidor: "Dexter."
Cliente: "�Qu� Dexter?"
Servidor: "La entrada de Dexter con ramas de acebo."
Cliente: "Gemido."
El ejemplo consiste en dos programas Java independientes ejecutandose: el programa cliente y el servidor. El programa cliente est� implementado por una s�la clase KnockKnockClient, y est� basado en el ejemplo EchoTest de la p�gina anterior. El programa servidor est� implementado por dos clases: KnockKnockServer y KKState. KnockKnockServer contiene el m�todo main() para el program servidor y realiza todo el trabajo duro, de escuchar el puerto, establecer conexiones, y leer y escribir a trav�s del socket. KKState sirve la bromas: sigue la pista de la broma actual, el estado actual (enviar konck knock, enviar pistas, etc...) y servir varias piezas de texto de la broma dependiendo del estado actual. Esta p�gina explica los detalles de cada clase en estos programas y finalmente le muestra c�mo ejecutarlas.
�El servidor Knock Knock
Esta secci�n pasa a trav�s del c�digo que implemente el programa servidor Knock Knock, Aqu� tienes el c�digo fuente completo de la clase KnockKnockServer.class. El programa servidor empieza creando un nuevo objeto ServerSocket para escuchar en un puerto espec�fico. Cuando escriba un servidor, deber�a elegir un puerto que no estuviera ya dedicado a otro servicio, KnockKnockServer escucha en el puerto 4444 porque sucede que el 4 es mi n�mero favorito y el puerto 4444 no est� siendo utilizado por ninguna otra cosa en mi entorno.
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.out.println("Could not listen on port: " + 4444 + ", " + e);
System.exit(1);
}
ServerSocket es una clase java.net que proporciona una implementaci�n independientes del sistema del lado del servidor de una conexi�n cliente/servidor. El constructor de ServerSocket lanza una excepci�n por alguna raz�n (c�mo que el puerto ya est� siendo utilizado) no puede escuchar en el puerto especificado. En este caso, el KnockKnockServer no tiene elecci�n pero sale.
Si el servidor se conecta con �xito con su puerto, el objeto ServerSocket se crea y el servidor continua con el siguiente paso, que es aceptar una conexi�n desde el cliente.
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.out.println("Accept failed: " + 4444 + ", " + e);
System.exit(1);
}
El m�todo accept() se bloquea (espera) hasta que un cliente empiece y pida una conexi�n el puerto (en este caso 4444) que el servidor est� escuchando. Cuando el m�todo accept() establece la conexi�n con �xito con el cliente, devuelve un objeto Socket que apunta a un puerto local nuevo. El servidor puede continuar con el cliente sobre este nuevo Socket en un puerto diferente del que estaba escuchando originalmente para las conexiones. Por eso el servidor puede continuar escuchando nuevas peticiones de clientes a trav�s del puerto original del ServerSocket. Esta versi�n del programa de ejemplo no escucha m�s peticiones de clientes. Sin embargo, una versi�n modificada de este programa, porpocionada m�s adelante, si lo hace.
El c�digo que hay dentro del siguiente bloque try implememte el lado del servidor de una comunicaci�n con el cliente. Esta secci�n del servidor es muy similar al lado del cliente (que vi� en el ejemplo de la p�gina anterior y que ver� m�s adelante en el ejemplo de la clase KnockKnockClient).
- Abre un stream de entrada y otro de salida sobre un socket.
- Lee y escribe a trav�s del socket.
Empecemos con las primeras 6 l�neas.
DataInputStream is = new DataInputStream(
new BufferedInputStream(clientSocket.getInputStream()));
PrintStream os = new PrintStream(
new BufferedOutputStream(clientSocket.getOutputStream(), 1024), false);
String inputLine, outputLine;
KKState kks = new KKState();
Las primeras dos l�neas del c�digo abren un stream de entrada sobre el socket devuelto por el m�todo accept(). Las siguiente dos l�neas abren un stream de salida sobre el mismo socket. La siguiente l�nea declara y crea un par de strings locales utilizadas para leer y escribir sobre el socket. Y finalmente, la �ltima l�nea crea un objeto KKState. Este es el objeto que sigue la pista de la broma actual, el estado actual dentro de una broma, etc.. Este objeto implementa el protocolo -- el lenguaje que el cliente y el servidor deben utilizar para comunicarse.
El servidor es el primero en hablar, con estas l�neas de c�digo.
outputLine = kks.processInput(null); os.println(outputLine); os.flush();
La primera l�nea de c�digo obtiene del objeto KKState la primera l�nea que el servidor le dice al cliente. Por ejemplo lo primero que el servidor dice es "Knock! Knock!".
Las siguientes dos l�neas escriben en el stream de salida conectado al socket del cliente y vac�a el stream de salida. Esta secuencia de c�digo inicia la conversaci�n entre el cliente y el servidor.
La siguiente secci�n de c�digo es un bucle que lee y escribe a trav�s del socket enviando y recibiendo mensajess entre el cliente y el servidor mientras que tengan que decirse algo el uno al otro. Como el servidor inicia la conversaci�n con un "Knock! Knock!", el servidor debe esperar la respuesta del cliente. As� el bucle while itera y lee del stream de entrada. El m�todo readLine() espera hasta que el cliente respondan algo escribiendo algo en el stream de salida (el stream de entrada del servidor). Cuando el cliente responde, el servidor pasa la respuesta al objeto KKState y le pide a �ste una respuesta adecuada. El servidor inmediatamente env�a la respuesta al cliente mediante el stream de salida conectado al socket, utilizando las llamadas a println() y flush(). Si la respuesta del servidor generada por el objeto KKState es "Bye.", indica que el cliente dijo que no quer�a m�s bromas y el bucle termina.
while ((inputLine = is.readLine()) != null) {
outputLine = kks.processInput(inputLine);
os.println(outputLine);
os.flush();
if (outputLine.equals("Bye."))
break;
}
La clase KnockKnockServer es un servidor de buen comportamiento, ya que las �ltimas l�neas de esta secci�n realizan la limpieza cerrando todas los streams de entrada y salida, el socket del cliente, y el socket del servidor.
os.close(); is.close(); clientSocket.close(); serverSocket.close();
�El Protocolo Knock Knock
La clase KKState implementa el protocolo que deben utilizar el cliente y el servidor para comunicarse. Esta clase sigue la pista de d�nde est�n el cliente y el servidor en su comunicaci�n y sirve las respuestas del servidor a las setencias del cliente. El objeto KKState contiene el texto de todos las bromas y se asegura de que el servidor ofrece la respuesta adecuada a las frases del cliente. No deber�a decir el cliente "�Qu� Dexter?" cuando el servidor dice "Knock! Knock!".
Todos las parejas cliente-servidor deden tener alg�n protocolo en el que hablar uno con otro, o el significado de los datos que se pasan unos a otro. El protocolo que utilicen sus clientes y servidores dependen enteramente de la comunicaci�n requerida por ellos para realizar su tarea.
�El Cliente Knock Knock
La clase KnockKnockClient implementa el programa cliente que habla con KnockKnockServer. KnockKnockClient est� basado en el programa EchoTest de la p�gina anterior y deber�a serte familiar. Pero echemos un vistazo de todas formas para ver lo que sucede en el cliente, mientras tenemos en mente lo que suced�a en el servidor.
Cuando arranca el program cliente, el servidor deber�a estar ya ejecut�ndose y escuchando el puerto esperando un petici�n de conexi�n por parte del cliente.
kkSocket = new Socket("taranis", 4444);
os = new PrintStream(kkSocket.getOutputStream());
is = new DataInputStream(kkSocket.getInputStream());
As�, lo primero que hace el programa cliente es abrir un socket sobre el puerto en el que est� escuchando el servidor en la m�quina en la que se est� ejecutando el servidor. El programa ejemplo KnockKnockClient abre un socket sobre el puerto 4444 que el mismo por el que est� escuchando el servidor. KnockKnockClient utiliza el nombre de host taranis, que es el nombre de una m�quina (hipot�tica) en tu red local. Cuando teclees y ejecutes este programa en tu m�quina, deber�as cambiar este nombre por el de una m�quina de tu red. Esta es la m�quina en la ejecutar� KnockKnockServer.
Luego el cliente abre un stream de entrada y otro de salida sobre el socket.
Luego comienza el bucle que implementa la comunicaci�n entre el cliente y el servidor. El servidor habla primero, por lo que el cliente debe escuchar, lo que hace leyendo desde el stream de entrada adosado al socket. Cuando el servidor habla, si dice "Bye,", el cliente sale del bucle. De otra forma muestra el texto en la salida estandard, y luego lee la respuesta del usuario, que la teclea en al entrada estandard. Despu�s de que el usuario teclee el retorno de carro, el cliente env�a el texto al servidor a trav�s del stream de salida adosado al socket.
while ((fromServer = is.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
while ((c = System.in.read()) != '\n') {
buf.append((char)c);
}
System.out.println("Client: " + buf);
os.println(buf.toString());
os.flush();
buf.setLength(0);
}
La comunicaci�n termina cuando el servidor pregunta si el cliente quiere escuchar otra broma, si el usuario dice no, el servidor dice "Bye.".
En el inter�s de una buena limpieza, el cliente cierra sus streams de entrada y salida y el socket.
os.close(); is.close(); kkSocket.close();
�Ejecutar los Programas
Primero se debe arrancar el programa servidor. Haz esto ejecutando el programa servidor utilizando el int�rprete de Java, como lo har�a con cualquier otro programa. Recuerda que debes ejecutarlo en la m�quina que el programa cliente especifica cuando crea el socket.
Luego ejecutas el programa cliente. Observa que puedes ejecuarlo en cualquier m�quina de tu red, no tiene porque ejecutarse en la misma m�quina que el servidor.
Si es demasiado r�pido, podr�a arrancar el cliente antes de que el servidor tuviera la oportunidad de incializarse y empezar a escuchar el puerto. Si esto sucede ver�s el siguiente mensaje de error cuando intentes arrancar el programa cliente.
Exception: java.net.SocketException: Connection refused
Si esto sucede, intenta ejecutar el programa cliente de nuevo.
Ver�s el siguiente mensaje de error si se te olvid� cambiar el nombre del host en el c�digo fuente del programa KnockKnockClient.
Trying to connect to unknown host: java.net.UnknownHostException: taranis
Modifica el programa KnockKnockClient y proporciona un nombre de host v�lido en tu red. Recompila el programa cliente e intentalo de nuevo.
Si intentas arrancar un segundo cliente mientras el primero est� conectado al servidor, el segundo colgar�. La siguiente secci�n le cuenta como soportar m�ltiples clientes.
Cuando obtengas una conexi�n entre el cliente y el servidor ver�s esto en tu pantalla.
Server: Knock! Knock!
Ahora, deber�s responder con .
Who's there?
El cliente repite lo que has tecleado y env�a el texto al servidor. El servidor responde con la primera l�nea de uno de sus varias bromas Knock Knock de su repertorio. Ahora tu pantalla deber�a contener esto (el texto que escribiste; est� en negrita).
Server: Knock! Knock! Who's there? Client: Who's there? Server: Turnip
Ahora deber�as responderle con.
Turnip who?
De nuevo, el cliente repite lo que has tecleado y env�a el texto al servidor. El servidor responde con la l�nea graciosa. Ahora tu pantalla deber�a contener esto (el texto que escribiste; est� en negrita).
Server: Knock! Knock! Who's there? Client: Who's there? Server: Turnip Turnip who? Client: Turnip who? Server: Turnip the heat, it's cold in here! Want another? (y/n)
Si quieres oir otra borma teclea "y", si no, teclee "n". Si tecleas "y", el servidor empieza de nuevo con "Knock! Knock!". Si tecleas "n" el servidor dice "Bye.", haciendo que tanto el cliente como el servidor terminen.
Si en cualquier momento cometes un error al teclear, el objeto KKState lo captura, el servidor responde con un mensaje similar a este, y empieza la broma otra vez.
Server: You're supposed to say "Who's there?"! Try again. Knock! Knock!
El objeto KKState es particular sobre la ortograf�a y la puntuaci�n, pero no sobre las letras may�sculas y min�sculas.
�Soportar M�tiples Clientes
El ejemplo KnockKnockServer fue dise�ado para escuchar y manejar una sola petici�n de conexi�n. Sin embargo, pueden recibirse varias peticiones sobre el mismo puerto y consecuentemente sobre el mismo ServeSocket. Las peticiones de conexiones de clientes se almecenan en el puerto, para que el servidor pueda aceptarlas de forma secuencial. Sin embargo, puede servirlas simultaneamente a trav�s del uso de threads -- un thread para procesar cada conexi�n de cliente.
El flujo l�gico b�sico en este servidor ser�a como este.
while (true) {
aceptar un a conexi�n;
crear un thread para tratar a cada cliente;
end while
El thread lee y escribe en la conexi�n del cliente cuando sea necesario.
Intenta esto: Modifica el KnockKnockServer para que pueda servir a varios clientes al mismo tiempo. Aqu� tienes nuestra soluci�n, que est� compuesta en dos clases. KKMultiServer y KKMultiServerThread. KKMultiServer hace un bucle contin�o escuchando peticiones de conexi�n desde los clientes en un ServerSocket. Cuando llega una petici�n KKMultiServer la accepta, crea un objeto KKMultiServerThread para procesarlo, manejando el socket devuelto por accept(), y arranca el thread. Luego el servidor vuelve a escuchar en el puerto las peticiones de conexi�n. El objeto KKMultiServerThread comunica con el cliente con el que est� leyendo y escribiendo a trav�s del socket. Ejecute el nuevo servidor Knock Knock y luego ejecuite varios clientes sucesivamente.