1. 概述

HTTP服务器通常为请求客户端提供资源。Java中已有多种生产级Web服务器,但通过实现一个基于ServerSocket的服务器,能帮助我们理解HTTP服务器的工作原理。

ServerSocket类允许我们创建一个监听特定IP地址和端口TCP连接的服务器。本文将学习如何使用ServerSocket构建简单服务器,并实现GET请求处理。⚠️ 注意:此服务器仅用于教学目的,不适合生产环境。

2. ServerSocket基础原理

服务器首先监听来自客户端的连接(可能是浏览器、API工具等)。连接建立后,服务器向客户端提供资源作为响应。

ServerSocket类提供在指定端口创建服务器的方法,通过accept()方法监听传入连接。该方法是阻塞式的,直到建立连接才会返回Socket实例。Socket实例提供了服务器与客户端通信的输入输出流通道。

3. 创建ServerSocket实例

首先创建指定端口的ServerSocket对象:

int port = 8080;
ServerSocket serverSocket = new ServerSocket(port);

然后使用accept()方法接受传入连接:

while (true) {
    Socket clientSocket = serverSocket.accept();
    //  ...
}

这段代码通过while循环持续等待连接。当连接建立时,accept()方法返回Socket对象,使服务器和客户端能通过已建立的网络通信

4. 处理输入输出

服务器需要接收客户端输入并返回响应。使用Socket类的getInputStream()getOutputStream()方法获取通信流

 while (true) {
    // ...
    BufferedReader in = new BufferedReader(
        new InputStreamReader(clientSocket.getInputStream())
    );
    BufferedWriter out = new BufferedWriter(
        new OutputStreamWriter(clientSocket.getOutputStream())
    ); 
    // ...
}

✅ 代码解析:

  • getInputStream()获取客户端-服务器连接的输入流,用BufferedReader包装高效读取文本数据
  • getOutputStream()BufferedWriter包装,方便服务器发送响应

输入通常是HTTP请求(如GET http://localhost:8080)。接下来构建响应:

String body = """
    <html>
        <head>
            <title>Baeldung Home</title>
        </head>
        <body>
            <h1>Baeldung Home Page</h1>
            <p>Java Tutorials</p>
            <ul>
                <li>
                    <a href="/get-started-with-java-series"> Java </a>
                </li>
                <li>
                    <a href="/spring-boot"> Spring </a>
                </li>
                <li>
                    <a href="/learn-jpa-hibernate"> Hibernate </a>
                </li>
            </ul>
         </body>
     </html>
""";

计算响应体长度用于HTTP头:

int length = body.length();

最后写入HTTP头和响应体:

 while (true) {
    // ...
    String clientInputLine;
    while ((clientInputLine = in.readLine()) != null) {
        if (clientInputLine.isEmpty()) {
            break;
        }

        out.write("HTTP/1.0 200 OK\r\n");
        out.write("Date: " + now + "\r\n");
        out.write("Server: Custom Server\r\n");
        out.write("Content-Type: text/html\r\n");
        out.write("Content-Length: " + length + "\r\n");
        out.write("\r\n");
        out.write(body);
    }
}

⚠️ 关键点:\r\n(空行)分隔HTTP头和响应体,这是HTTP协议的硬性要求。

5. 多线程服务器

单线程服务器性能堪忧,生产级服务器必须支持并发请求处理。下面重构为多线程版本:

class SimpleHttpServerMultiThreaded {

    private final int port;
    private static final int THREAD_POOL_SIZE = 10;

    public SimpleHttpServerMultiThreaded(int port) {
        this.port = port;
    } 
}

定义客户端处理方法:

void handleClient(Socket clientSocket) {
    try (BufferedReader in = new BufferedReader(
            new InputStreamReader(clientSocket.getInputStream()));
        BufferedWriter out = new BufferedWriter(
            new OutputStreamWriter(clientSocket.getOutputStream()))
    ) {

        String clientInputLine;
        while ((clientInputLine = in.readLine()) != null) {
            if (clientInputLine.isEmpty()) {
                break;
            }
        }
        LocalDateTime now = LocalDateTime.now();

        out.write("HTTP/1.0 200 OK\r\n");
        out.write("Date: " + now + "\r\n");
        out.write("Server: Custom Server\r\n");
        out.write("Content-Type: text/html\r\n");
        out.write("Content-Length: " + length + "\r\n");
        out.write("\r\n");
        out.write(body);

    } catch (IOException e) {
        // ...
    } finally {
        try {
            clientSocket.close();
        } catch (IOException e) {
            // ...
        }
    }
}

创建启动方法处理并发连接:

void start() throws IOException {
    try (ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        ServerSocket serverSocket = new ServerSocket(port)) {

        while (true) {
            Socket clientSocket = serverSocket.accept();
            threadPool.execute(() -> handleClient(clientSocket));
        }
    }
}

✅ 核心改进:

  • 使用ExecutorService创建固定大小线程池
  • 通过execute()提交连接处理任务
  • 每次客户端连接都会创建新的Socket实例,确保通信通道独立

6. 服务器测试

main方法中启动服务器:

static void main(String[] args) throws IOException {
    int port = 8080;
    SimpleHttpServerMultiThreaded server = new SimpleHttpServerMultiThreaded(port);
    server.start();
}

浏览器访问http://localhost:8080测试:

HTTP服务器测试页面

7. 总结

本文通过ServerSocket实现了简单HTTP服务器,包括:

  • 单线程服务器基础实现
  • 多线程服务器性能优化
  • HTTP请求/响应处理流程

虽然代码简单粗暴,但完整展示了HTTP服务器的核心原理。生产环境中建议使用成熟的Web服务器框架(如Tomcat、Netty),避免重复造轮子。