icono_twiter icono LinkedIn icono Facebook Icono Xing
Roberto Canales Mora

Creador y propietario de AdictosAlTrabajo.com, Director General de Autentia S.L., Ingeniero Técnico de Telecomunicaciones y Executive MBA por el Instituto de Empresa 2007.
Twitter:

Autor de los Libros: Planifica tu éxito: de aprendiz a empresario y Informática profesional, las reglas no escritas para triunfar en la empresa

Puedes consultar mi CV y alguna de mis primeras aplicaciones (de los 90) aquí

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2003-06-17

Tutorial visitado 69.413 veces Descargar en PDF

Creación de nuestro propio servidor Web

Vamos a escribir un pequeño programa para demostrar las capacidades de MultiThread de Java así como exponer de un modo sencillo otras características como son la creación de aplicaciones cliente-servidor con sockets, la escritura en Streams y el control de excepciones, etc ...

Esqueleto básico

Primero vamos a escribir el esqueleto de nuestro programa para no tener que ir repitiendo grandes trozo de código. Este esqueleto podeis observar que esta vacio y no es muy funcional, aunque ya lo iremos rellenando a medida que haga falta.

Lo más importante es el concepto que vamos a tener una función que se llama arranca, dentro de una clase que se habrá encargado de leer los parámetros que le hayan pasado desde la línea de comandos y que disponemos de un método para centralizar los logs.... lo demás ya veremos

 

public class servidorWeb
{
	int puerto = 90;

	final int ERROR = 0;
	final int WARNING = 1;
	final int DEBUG = 2;

        void depura(String mensaje) // los mensajes por defecto serán en modo depuracion
	{
		depura(mensaje,DEBUG);
	} 

	// funcion para centralizar los mensajes de depuración
        	void depura(String mensaje, int gravedad)
        	{
        System.out.println("Mensaje: " + mensaje);
	} 

	// punto de entrada a nuestro programa
        public static void main(String [] array) 
	{
		servidorWeb instancia = new servidorWeb(array); 
		instancia.arranca();
	}

	// constructor que interpreta los parameros pasados
        servidorWeb(String[] param)
	{
		procesaParametros(); 
	}

	// parsearemos el fichero de entrada y estableceremos las variables de clase
        boolean procesaParametros()
	{
		return true; 
	}

        boolean arranca()
	{
		depura("Arrancamos nuestro servidor",DEBUG);
		return true;
	}

}

 

Bueno, lo primero que tenemos que hacer, es quedarnos a la espera en un puerto, creando un Socket de servidor.

Cuando un navegador nos envie una pertición, la procesamos y mostramos por pantalla

 

boolean arranca()
{
	depura("Arrancamos nuestro servidor",DEBUG);
	try
	{
		ServerSocket s = new ServerSocket(90);
		depura("Quedamos a la espera de conexion");
		Socket entrante = s.accept();
		depura("Procesamos conexion");
		BufferedReader in = new BufferedReader (new InputStreamReader(entrante.getInputStream()));
		String cadena = "";

		while (cadena != null)
		{
			cadena = in.readLine();
			if (cadena != null)
			{
				depura("--" + cadena);
			}
		}

		depura("Hemos terminado");

	}
	catch(Exception e)
	{
		depura("Error en servidor\n" + e.toString());
	}
		return true;
	}
}
	
      

Si desde un navegador, realizamos una petición a este estilo

En nuetro log veremos

Mensaje: Arrancamos nuestro servidor
Mensaje: Quedamos a la espera de conexion
Mensaje: Procesamos conexion
Mensaje: --GET / HTTP/1.0
Mensaje: --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Mensaje: --Accept-Language: es
Mensaje: --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Mensaje: --Host: localhost:90
Mensaje: --Connection: Keep-Alive
Mensaje: --

Si dejamos tal cual este programa, nos puede ser muy útil en ciertas circunstancias... por ejemplo, como hemos visto en el caso anterior, para comprobar que manda nuestro navegador a un sitio Web (para depurar problemas)

Por ejemplo .... quiero saber que información arrastra mi navegador cuando se conecta a un phpNuke (gestor de presentación de contenidos gratuito creado en php).

Ataco a mi máquina donde tengo phpNuke

Luego me conecto a mi misma máquina al puerto de mi programa ...

Mensaje: --GET / HTTP/1.0
Mensaje: --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Mensaje: --Accept-Language: es
Mensaje: --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Mensaje: --Host: localhost:90
Mensaje: --Connection: Keep-Alive
Mensaje: --Cookie: ASPSESSIONIDCCTBSQBA=GBDFCHBDFKMNDPBBDFPJIMIO
Mensaje: --

Y sorpresa .... compruebo que me genera un cookie ......... para mantener la sensación de sesión ......

(a algunos seguro que se les ha hecho los ojos chirivitas ......por el posible problema de seguridad cuando navegais de un Web a otro ..... ) seguro a partir de ahora ... cuando esteis logados en un servidor ..... pinchareis al botón "cerrar sesión"  antes de navegar por otros Webs .....

 

Dotar de funcionalidad nuestro servidor Web

No se si ya habeís tenido la percepción de que  nuestro programa tiene algunas deficiencias:

  • Primero ... que no vemos que nos pide el usuario y no se lo enviamos ......

  • Segundo .... nuestro programa solo sirve una petición y se apaga .....

  • Tercero .... como resolveríamos la situación (normal) de que más de un usuario nos realizase peticiones simultaneas .....

He aquí donde ya identificamos ciertas necesidades que vamos a tratar de resolver.

Multiproceso

Vamos por cachos .... primero empezamos por los problemas más graves.... vamos a hacer que nuestra aplicación sea multi-hilo (multi-thread) de tal modo que vamos a crear una nueva clase ... que derive de Thread y que sea capaz de atender cada petición en paralelo que recibamos

Modificamos la clase principal:


boolean arranca()
{
	depura("Arrancamos nuestro servidor",DEBUG);

	try
	{
		ServerSocket s = new ServerSocket(90);
		depura("Quedamos a la espera de conexion");

		while(true)  // bucle infinito .... ya veremos como hacerlo de otro modo
		{
			Socket entrante = s.accept();
			peticionWeb pCliente = new peticionWeb(entrante);
			pCliente.start();
		}

	}
	catch(Exception e)
	{
		depura("Error en servidor\n" + e.toString());
	}

	return true;
}

      

Ahora la clase auxiliar

class peticionWeb extends Thread
{
	final int ERROR = 0;
	final int WARNING = 1;
	final int DEBUG = 2;

	void depura(String mensaje)
	{
		depura(mensaje,DEBUG);
	}

	void depura(String mensaje, int gravedad)
	{
		System.out.println(currentThread().toString() + " - " + mensaje);
	}

	private Socket scliente 	= null;		// representa la petición de nuestro cliente
   	private PrintWriter out 	= null;		// representa el buffer donde escribimos la respuesta

   	peticionWeb(Socket ps)
   	{
		scliente = ps;
		setPriority(NORM_PRIORITY - 1); // hacemos que la prioridad sea baja
   	}

	public void run() // emplementamos el metodo run
	{
		depura("Procesamos conexion");

		try
		{
			BufferedReader in = new BufferedReader 
				(new InputStreamReader(scliente.getInputStream()));
			
			String cadena = "";

			while (cadena != null || cadena !="")
			{
				cadena = in.readLine();
				if (cadena != null )
				{
					depura("--" + cadena);
				}
			}
		}
		catch(Exception e)
		{
			depura("Error en servidor\n" + e.toString());
		}

		depura("Hemos terminado");
	}

}

Si analizamos la respuesta de nuestro Web ante varias peticiones:

--------------------Configuration: C:\java\jakarta-tomcat-4.1.12\common\lib roberto--------------------
Mensaje: Arrancamos nuestro servidor
Mensaje: Quedamos a la espera de conexion
Thread[Thread-1,4,main] - Procesamos conexion
Thread[Thread-1,4,main] - --GET / HTTP/1.0
Thread[Thread-1,4,main] - --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Thread[Thread-1,4,main] - --Accept-Language: es
Thread[Thread-1,4,main] - --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Thread[Thread-1,4,main] - --Host: localhost:90
Thread[Thread-1,4,main] - --Connection: Keep-Alive
Thread[Thread-1,4,main] - --Cookie: ASPSESSIONIDCCTBSQBA=GBDFCHBDFKMNDPBBDFPJIMIO
Thread[Thread-1,4,main] - --
Thread[Thread-1,4,main] - Error en servidor
java.net.SocketException: Connection reset
Thread[Thread-1,4,main] - Hemos terminado
Thread[Thread-2,4,main] - Procesamos conexion
Thread[Thread-2,4,main] - --GET /?a=1 HTTP/1.0
Thread[Thread-2,4,main] - --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Thread[Thread-2,4,main] - --Accept-Language: es
Thread[Thread-2,4,main] - --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Thread[Thread-2,4,main] - --Host: localhost:90
Thread[Thread-2,4,main] - --Connection: Keep-Alive
Thread[Thread-2,4,main] - --Cookie: ASPSESSIONIDCCTBSQBA=GBDFCHBDFKMNDPBBDFPJIMIO
Thread[Thread-2,4,main] - --
Thread[Thread-2,4,main] - Error en servidor
java.net.SocketException: Connection reset
Thread[Thread-2,4,main] - Hemos terminado
Thread[Thread-3,4,main] - Procesamos conexion
Thread[Thread-3,4,main] - --GET /?a=3 HTTP/1.0
Thread[Thread-3,4,main] - --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Thread[Thread-3,4,main] - --Accept-Language: es
Thread[Thread-3,4,main] - --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Thread[Thread-3,4,main] - --Host: localhost:90
Thread[Thread-3,4,main] - --Connection: Keep-Alive
Thread[Thread-3,4,main] - --Cookie: ASPSESSIONIDCCTBSQBA=GBDFCHBDFKMNDPBBDFPJIMIO
Thread[Thread-3,4,main] - --

Así, es posible que no tengamos la percepción de multi-hilo. Vamos a poner un retardo en cada petición para ver como se intercalan en el log

Modificamos el programa:

while (cadena != null || cadena !="")
{
	cadena = in.readLine();
	if (cadena != null )
	{
		sleep(500);
		depura("--" + cadena);
	}
	}
}
      

Vemos el Log donde comprobamos la concurrencia ..... 

--------------------Configuration: C:\java\jakarta-tomcat-4.1.12\common\lib roberto--------------------
Mensaje: Arrancamos nuestro servidor
Mensaje: Quedamos a la espera de conexion
Thread[Thread-1,4,main] - Procesamos conexion
Thread[Thread-1,4,main] - --GET /?a=1 HTTP/1.0
Thread[Thread-1,4,main] - --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*

Thread[Thread-1,4,main] - --Accept-Language: es
Thread[Thread-1,4,main] - --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Thread[Thread-2,4,main] - Procesamos conexion
Thread[Thread-1,4,main] - --Host: localhost:90
Thread[Thread-2,4,main] - --GET /?a=2 HTTP/1.0
Thread[Thread-1,4,main] - --Connection: Keep-Alive
Thread[Thread-2,4,main] - --Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Thread[Thread-1,4,main] - --Cookie: ASPSESSIONIDCCTBSQBA=GBDFCHBDFKMNDPBBDFPJIMIO
Thread[Thread-2,4,main] - --Accept-Language: es
Thread[Thread-1,4,main] - --
Thread[Thread-1,4,main] - Error en servidor
java.net.SocketException: Connection reset
Thread[Thread-1,4,main] - Hemos terminado

Thread[Thread-2,4,main] - --User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
Thread[Thread-2,4,main] - --Host: localhost:90
Thread[Thread-2,4,main] - --Connection: Keep-Alive
Thread[Thread-2,4,main] - --Cookie: ASPSESSIONIDCCTBSQBA=GBDFCHBDFKMNDPBBDFPJIMIO
Thread[Thread-2,4,main] - --

Retornar una respuesta

Ahora vamos a incluir la funcionalidad para retornar la página que no solicita el usuario.


public void run() // emplementamos el metodo run
{
	depura("Procesamos conexion");

	try
	{
		BufferedReader in = new BufferedReader 
			(new InputStreamReader(scliente.getInputStream()));
		out = new PrintWriter
			(new OutputStreamWriter(scliente.getOutputStream(),"8859_1"),true) ;


		String cadena = "";	// cadena donde almacenamos las lineas que leemos
		int i=0;	// lo usaremos para que cierto codigo solo se ejecute una vez

		do
		{
			cadena = in.readLine();

			if (cadena != null )
			{
				// sleep(500);
				depura("--" + cadena + "-");
			}


     		  if(i == 0) // la primera linea nos dice que fichero hay que descargar
		  {
		        i++;

		        StringTokenizer st = new StringTokenizer(cadena);

               		 if ((st.countTokens() >= 2) && st.nextToken().equals("GET"))
               		 {
               		 	retornaFichero(st.nextToken()) ;
               		 }
              		 else
             		 {
             		   	out.println("400 Petición Incorrecta") ;
                	}
		    }

		}
		while (cadena != null && cadena.length() != 0);

	}
	catch(Exception e)
	{
			depura("No encuentro el fichero " + mifichero.toString());
	      	out.println("HTTP/1.0 400 ok");
	      	out.close();
	}

	depura("Hemos terminado");
}

      

Y añadimos el nuevo método


void retornaFichero(String sfichero)
{
	depura("Recuperamos el fichero " + sfichero);

	// comprobamos si tiene una barra al principio
	if (sfichero.startsWith("/"))
	{
		sfichero = sfichero.substring(1) ;
	}

    // si acaba en /, le retornamos el index.htm de ese directorio
    // si la cadena esta vacia, no retorna el index.htm principal
    if (sfichero.endsWith("/") || sfichero.equals(""))
    {
    	sfichero = sfichero + "index.htm" ;
    }

    try
    {

	    // Ahora leemos el fichero y lo retornamos
	    File mifichero = new File(sfichero) ;

	    if (mifichero.exists())
	    {
  			out.println("HTTP/1.0 200 ok");
			out.println("Server: Roberto Server/1.0");
			out.println("Date: " + new Date());
			out.println("Content-Type: text/html");
			out.println("Content-Length: " + mifichero.length());
			out.println("\n");

			BufferedReader ficheroLocal = new BufferedReader
					(new FileReader(mifichero));


			String linea = "";

			do
			{
				linea = ficheroLocal.readLine();

				if (linea != null )
				{
					// sleep(500);
					out.println(linea);
				}
			}
			while (linea != null);

			depura("fin envio fichero");

			ficheroLocal.close();
			out.close();

		}  // fin de si el fiechero existe
		else
		{
			depura("No encuentro el fichero " + mifichero.toString());
		}
	}
	catch(Exception e)
	{
		depura("Error al retornar fichero");
	}
}
      

 

Puede descargarse el código fuente aquí

Sobre el Autor ..

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL:

Fecha publicación: 2011-11-16-00:53:08

Autor: roncamixtito

como puedo hacer para que mi servidor web, hecho en java, lea archivos php
?¿?
me respondes a mi correo ronaldots@live.com porfavor

Fecha publicación: 2010-03-10-19:10:17

Autor: manugaon

Hola, antes que nada, agradecer la aportacion de este ejemplo, es muy básica y ayuda a los que estamos empezando en estos temas de aplicaciones servidor...
actualmente estoy programando un servidor que atiende una serie de peticiones, pero me pregunto porque, al igual que en el ejemplo, a veces fallan las conexiones con los clientes.
Alquien podria ayudarme? Muchas gracias de antemano

Fecha publicación: 2009-05-22-12:17:16

Autor:

[Carlos] Hola Señoron Robertor Canales, gracias por sacarme de dudas. La verdad he estado checando esto de los servidores, por que queria hacer uno que funcionase desde un CD-ROM. Pero tiene toda la razón, cuando menciona que "la cosa se complica" estube checando lo de los mime-types y creo que mejor opto por uno ya hecho (jajajaja), como el microweb, usbWebServer, Xerver. Gracias por su ayuda, me fue de mucha utilidad. Bye...

Fecha publicación: 2009-05-22-10:57:07

Autor:

[rcanales] Hola: El tutorial es más un ejemplo de programación que un servidor Web real. Hay decenas de servidores completos disponibles en internet en código fuente. Yo te sugeriría que usases otro como base si quieres hacer algo serio (porque la cosa luego se complica). Uno de los problemas es que si te fijas, siempre retorna el tipo como texto: out.println("Content-Type: text/html"); Para que funcione correctamente con imagenes tendrias que considerar los mime-types y devolver el contenido en binario. Un saludo

Fecha publicación: 2009-05-22-10:38:46

Autor:

[carlos] Hola que tal, tengo una duda o un problema con el servidor web realizado en java... Segui todos los pasos y me funciona bastante bien, pero yo quiero que se muestren las imagenes (cosa que no sucede) el cmd me muestra que no se encontraron los siguientes ficheros: stdlib y dowload.css Si me pudiera ayudar le estare agradecido. Gracias!!!

Fecha publicación: 2006-06-08-04:26:10

Autor:

[Victor Hugo Fernande] en verdad son una buena manera de darnos una oportunidad de aprendizaje autodidacta el tutorial es genial a mi me ha servido en gran medida espero y sigan asi por mucho tiempo y por supuesto nos den la oportunidad de ser autodidactas atraves de sus tutoriales, por su labor desiteresada y su funcion altruista de enseñanza un millon de gracias, personalmente gracias no hay otra palabra para expresar el agadecimiento por lo que hacen y la oportunidad que nos dan gracias.