08.05.2013 Views

PRÁCTICA OPTATIVA 1: SERVIDOR WEB MULTITHREAD - NET16

PRÁCTICA OPTATIVA 1: SERVIDOR WEB MULTITHREAD - NET16

PRÁCTICA OPTATIVA 1: SERVIDOR WEB MULTITHREAD - NET16

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Práctica 1: Introducción a Wireshark Redes de Ordenadores<br />

<strong>PRÁCTICA</strong> <strong>OPTATIVA</strong> 1: <strong>SERVIDOR</strong> <strong>WEB</strong><br />

<strong>MULTITHREAD</strong><br />

Al terminar esta práctica habrás desarrollado, en Java, un servidor web multithread que será capaz de<br />

responder a múltiples peticiones en paralelo. Para esto implementarás un servidor HTTP 1.0 definido en la RFC<br />

1945.<br />

HTTP/1.0 crea una conexión TCP nueva para cada par petición/respuesta. Un thread es el encargado de<br />

manejar cada una de dichas conexiones. Habrá además un thread principal en el que el servidor esperará las<br />

peticiones de nuevos clientes que quieran establecer nuevas conexiones. Para simplificar la tarea de<br />

programación, separaremos el desarrollo del código en dos secciones. En la primera sección desarrollaras un<br />

servidor multithread que simplemente imprimirá el contenido de la petición HTTP que recibe. Una vez que este<br />

programa esté funcionando correctamente debes escribir el código que generará la respuesta apropiada.<br />

Mientras desarrollas el código puedes probarlo con un navegador web (Firefox, Chrome, etc.), recuerda que tu<br />

servidor web no tendrá salida a internet por razones de seguridad, además tampoco podrá funcionar en el<br />

puerto 80 que es el estándar para la web, por ello tendrás que especificar el host y el puerto en el navegador<br />

de una forma algo diferente, por ejemplo si estás ejecutando el servidor en un ordenador del laboratorio en el<br />

puerto 6789 y quieres obtener el fichero index.html, tendrás que especificar la dirección de la siguiente<br />

manera:<br />

http://localhost:8080/index.html<br />

Cuando el servidor encuentre un error debe enviar un mensaje de respuesta HTML apropiado para que la<br />

información se muestre correctamente en el navegador.<br />

- 1 -


PARTE 1<br />

Práctica 1: Introducción a Wireshark Redes de Ordenadores<br />

A través de los siguientes pasos desarrollarás el código del servidor web, habrá algunos detalles que deberás<br />

escribir tu.<br />

Nuestra primera implementación será multithread, donde el proceso de cada petición entrante se hará en un<br />

thread de ejecución separado. Esto permite que el servidor tenga múltiples clientes en paralelo o que envíe<br />

varios ficheros simultáneos al mismo cliente. Cuando se crea un thread de ejecución es necesario pasarle al<br />

constructor una instancia de una clase que implemente la interfaz Runnable. Esta es la razón por la que se crea<br />

una clase separada llamada HttpRequest. La estructura de este servidor se muestra en el código 1:<br />

Importjava.io.* ;<br />

import java.net.* ;<br />

import java.util.* ;<br />

public final class WebServer {<br />

public static void main(String argv[]) throws Exception {<br />

. . .<br />

}<br />

}<br />

final class HttpRequest implements Runnable {<br />

. . .<br />

}<br />

Código 1: estructura del servidor<br />

Normalmente los servidores web atienden a sus peticiones a través del puerto 80. En este caso puedes escoger<br />

cualquier puerto a partir del 1024 (para elegir puertos menores es necesario tener permisos de administrador).<br />

Debes recordar el puerto para hacer peticiones desde tu navegador web.<br />

Lo siguiente que hay que hacer es abrir un socket y dejarlo esperando a que llegue una conexión TCP,<br />

queremos que el servidor esté constantemente funcionando y por ello ponemos un bucle infinito, esto significa<br />

que para cerrar el servidor habrá que teclear ctrl+c, se puede ver la idea en el código 2.<br />

public static void main(String argv[]) throws Exception {<br />

// Set the port number.<br />

int port = 6789;<br />

}<br />

// Establish the listen socket.<br />

. . .<br />

// Process HTTP service requests in an infinite loop.<br />

while (true) {<br />

// Listen for a TCP connection request.<br />

. . .<br />

}<br />

Código 2: programa principal<br />

- 2 -


Práctica 1: Introducción a Wireshark Redes de Ordenadores<br />

Cuando se recibe una petición, creamos un objeto HttpRequest al que hay que pasarle una referencia al socket<br />

que representa la conexión establecida con el cliente.<br />

Para que el HttpRequest pueda responder a la petición HTTP entrante en un thread separado hay que crear un<br />

objeto Thread y pasarle a su constructor una referencia al objeto HttpRequest, finalmente llamamos al método<br />

start().<br />

// Construct an object to process the HTTP<br />

request message.<br />

HttpRequest request = new HttpRequest( . . . );<br />

// Create a new thread to process the request.<br />

Thread thread = new Thread(request);<br />

// Start the thread.<br />

thread.start();<br />

Código 3: Creación del thread<br />

Una vez que se ha creado el thread y este ha empezado a ejecutar, el thread principal vuelve al inicio del bucle<br />

while a esperar otra conexión TCP mientras que el nuevo thread continua ejecutándose. Cuando se reciba otra<br />

petición se repetirá el proceso. Con esto se completa el código de main, ahora falta desarrollar la clase<br />

HttpRequest.<br />

Para la clase HttpRequest definimos la variable CRLF porque cada línea debe terminar en retorno de carro (CR)<br />

y nueva línea (LF), la variable socket guarda una referencia a la conexión.<br />

final class HttpRequest implements Runnable {<br />

final static String CRLF = "\r\n";<br />

Socket socket;<br />

// Constructor<br />

}<br />

public HttpRequest(Socket socket) throws Exception<br />

{<br />

this.socket = socket;<br />

//Implement the run() method of the<br />

//Runnable interface.<br />

public voi run() {<br />

. . .<br />

}<br />

private void processRequest() throws Exception {<br />

. . .<br />

}<br />

Código 4: estructura de la clase HttpRequest<br />

El proceso de las peticiones se debe hacer en el método processRequest que será llamado dentro de run,<br />

recuerda que debes tener en cuenta las excepciones en este método.<br />

- 3 -


Práctica 1: Introducción a Wireshark Redes de Ordenadores<br />

Para procesar el mensaje hay que obtener acceso a los streams de entrada y salida del socket, hay que poner<br />

filtros al stream de entrada para poder leer correctamente la petición pero no en el de salida, ya que<br />

escribiremos cadenas de bytes directamente.<br />

Código 5: streams de entrada y salida<br />

Ahora estamos preparados para obtener la petición del cliente leyendo del socket, para ello usaremos el<br />

método readLine() que lee caracteres hasta encontrar un fin de línea. Deberás leer e imprimir todas las líneas<br />

de la petición.<br />

Finalmente recuerda que tanto los streams como el socket deben ser cerrados una vez que termina el proceso.<br />

PARTE 2<br />

private void processRequest() throws Exception {<br />

// Get a reference to the socket's input and output<br />

// streams.<br />

InputStream is = ?;<br />

DataOutputStream os = ?;<br />

}<br />

// Set up input stream filters.<br />

?<br />

BufferedReader br = ?;<br />

. . .<br />

// Close streams and socket.<br />

os.close();<br />

br.close();<br />

socket.close();<br />

Código 6: cierre de streams y sockets<br />

En esta parte habrá que procesar la petición HTTP para saber que fichero se está pidiendo, posteriormente<br />

construir la respuesta y enviarla. En este caso solo vamos a considerar las peticiones GET y ignoraremos el tipo<br />

de petición (HEAD o POST).<br />

Las respuestas HTTP tienen tres partes, el estatus, la cabecera y el cuerpo. El estatus y las cabeceras van<br />

seguidas de la secuencia CRLF. En caso de que el fichero pedido no exista el estatus debe ser 404 Not Found e<br />

incluir un mensaje de error en el cuerpo.<br />

- 4 -


Práctica 1: Introducción a Wireshark Redes de Ordenadores<br />

// Construct the response message.<br />

String statusLine = null;<br />

String contentTypeLine = null;<br />

String entityBody = null;<br />

if (fileExists) {<br />

statusLine = ?;<br />

contentTypeLine = "Content-type: " + ? + CRLF;<br />

} else {<br />

statusLine = ?;<br />

contentTypeLine = ?;<br />

entityBody = "Not Found" +<br />

"Not Found";<br />

statusLine = ?;<br />

contentTypeLine = "Content-type: " + contentType(fileName) +<br />

CRLF;<br />

}<br />

Código 7: Construcción de la respuesta http<br />

Tras esto el servidor ya puede enviar la respuesta, consideraremos que el único fichero existente es index.html<br />

que tendrá un mensaje cualquiera en formato HTML.<br />

- 5 -

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!