Tabla de Contenidos

04 - Servidor HTTP - 2

Servidor HTTP multicliente con ficheros estáticos

En el tema anterior vimos cómo crear un mini servidor HTTP muy básico.

Ahora vamos a mejorarlo para que:

Funcionamiento

  1. El servidor escucha en el puerto 8080.

  2. Cada vez que un cliente se conecta, se crea un hilo independiente para manejar su solicitud.

  3. Si el cliente solicita `/`, se envía el fichero `index.html`.

  4. Si solicita otro recurso (por ejemplo `/about.html`), se busca en la carpeta `www`.

  5. Si el archivo no existe, se devuelve un 404 Not Found.

Estructura del proyecto

proyecto/
 ├─ src/main/java/es/cesguiro/socket/HttpServerMulti.java
 ├─ src/main/java/es/cesguiro/socket/ClientHandler.java
 ├─ src/main/java/es/cesguiro/socket/Main.java
 └─ src/main/resources/www/
      ├─ index.html
      └─ about.html

Código

package es.cesguiro.socket;

import java.net.ServerSocket;
import java.net.Socket;

public class HttpServerMulti {

    private final int port = 8080;

    public void start() {
        System.out.println("Starting HTTP server on port " + port + "...");
        try {
            // Iniciar el servidor y aceptar múltiples conexiones
            ServerSocket serverSocket = new ServerSocket(port);
            System.out.println("HTTP server started. Waiting for requests...");

            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("Client connected: " + clientSocket.getInetAddress());

                // Manejar la conexión en un nuevo hilo
                new Thread(new ClientHandler(clientSocket)).start();
            }
        } catch (Exception e) {
            System.err.println("Error starting HTTP server: " + e.getMessage());
            throw new RuntimeException("Failed to start HTTP server", e);
        }
    }
}

package es.cesguiro.socket;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class ClientHandler implements Runnable {

    private final Socket clientSocket;

    public ClientHandler(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    @Override
    public void run() {
        try (
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)
        ) {
            // Leer la primera línea de la solicitud HTTP
            String requestLine = in.readLine();
            System.out.println("Received request: " + requestLine);

            if (requestLine == null || !requestLine.startsWith("GET")) {
                sendError(out, 400, "Bad Request");
                return;
            }

            // Extraer recurso solicitado
            String[] parts = requestLine.split(" ");
            String resource = parts.length > 1 ? parts[1] : "/";
            if (resource.equals("/")) {
                resource = "/index.html";
            }

            // Buscar archivo en resources/www
            InputStream fileStream = getClass().getResourceAsStream("/www" + resource);
            if (fileStream != null) {
                String body = new String(fileStream.readAllBytes(), StandardCharsets.UTF_8);
                sendResponse(out, 200, "OK", body);
            } else {
                sendResponse(out, 404, "Not Found", "<h1>404 Not Found</h1>");
            }

        } catch (IOException e) {
            System.err.println("Error handling client connection: " + e.getMessage());
        } finally {
            try {
                clientSocket.close();
            } catch (Exception e) {
                System.err.println("Error closing client socket: " + e.getMessage());
            }
        }
    }

    private void sendResponse(PrintWriter out, int statusCode, String statusText, String body) {
        out.print("HTTP/1.0 " + statusCode + " " + statusText + "\r\n");
        out.print("Content-Type: text/html\r\n");
        out.print("Content-Length: " + body.getBytes(StandardCharsets.UTF_8).length + "\r\n");
        out.print("\r\n");
        out.print(body);
        out.flush();
    }

    private void sendError(PrintWriter out, int statusCode, String statusText) {
        sendResponse(out, statusCode, statusText, "<h1>" + statusCode + " " + statusText + "</h1>");
    }
}

package es.cesguiro.socket;

public class Main {
    public static void main(String[] args) {
        HttpServerMulti server = new HttpServerMulti();
        server.start();
    }
}

Archivos HTML

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Bienvenido</title>
</head>
<body>
    <h1>Hola desde el Servidor HTTP multicliente</h1>
    <p>Este es el archivo <strong>index.html</strong> servido desde <code>src/main/resources/www</code>.</p>
    <p><a href="/about.html">Ir a la página About</a></p>
</body>
</html>

about.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Acerca de</title>
</head>
<body>
    <h1>Página About</h1>
    <p>Este servidor es un ejemplo básico de manejo de múltiples conexiones y ficheros estáticos en Java.</p>
    <p><a href="/">Volver al inicio</a></p>
</body>
</html>

Ejecución

  1. Arranca el servidor (`Main.java`).

  2. Abre un navegador y accede a: