PRÁCTICA OPTATIVA 1: SERVIDOR WEB MULTITHREAD - NET16
PRÁCTICA OPTATIVA 1: SERVIDOR WEB MULTITHREAD - NET16
PRÁCTICA OPTATIVA 1: SERVIDOR WEB MULTITHREAD - NET16
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 -