侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个支持多线程的简易HTTP服务器

2025-12-5 / 0 评论 / 6 阅读

题目

实现一个支持多线程的简易HTTP服务器

信息

  • 类型:问答
  • 难度:⭐⭐

考点

Socket编程,HTTP协议理解,多线程处理,资源管理

快速回答

实现要点:

  • 使用ServerSocket监听端口,循环接受客户端连接
  • 为每个连接创建独立线程处理请求
  • 解析HTTP请求行(GET /path HTTP/1.1)
  • 根据请求路径返回静态资源或404响应
  • 正确设置响应头(Content-Type, Content-Length)
  • 使用线程池优化线程管理
  • 确保资源关闭(try-with-resources)
## 解析

原理说明

HTTP服务器基于TCP协议,通过Socket建立连接。核心流程:

  1. 创建ServerSocket绑定端口
  2. 循环调用accept()接收客户端连接
  3. 为每个Socket创建独立线程处理请求响应
  4. 解析HTTP请求报文(重点关注请求行)
  5. 根据请求路径查找资源并生成响应
  6. 遵循HTTP协议格式返回响应

代码示例

import java.net.*;
import java.io.*;
import java.util.concurrent.*;

public class SimpleHttpServer {
    private static final int PORT = 8080;
    private static final int THREAD_POOL_SIZE = 10;
    private static final String DOC_ROOT = "./resources";

    public static void main(String[] args) throws IOException {
        ExecutorService pool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server started on port " + PORT);
            while (true) {
                Socket clientSocket = serverSocket.accept();
                pool.execute(() -> handleRequest(clientSocket));
            }
        }
    }

    private static void handleRequest(Socket clientSocket) {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             OutputStream out = clientSocket.getOutputStream()) {

            // 解析请求行
            String requestLine = in.readLine();
            if (requestLine == null) return;
            String[] parts = requestLine.split(" ");
            if (parts.length < 3) return;
            String method = parts[0], path = parts[1];

            // 处理GET请求
            if ("GET".equals(method)) {
                File file = new File(DOC_ROOT + path);

                if (file.exists() && !file.isDirectory()) {
                    sendResponse(out, "200 OK", "text/html", Files.readAllBytes(file.toPath()));
                } else {
                    sendResponse(out, "404 Not Found", "text/plain", "Resource not found".getBytes());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void sendResponse(OutputStream out, String status, 
                                     String contentType, byte[] content) throws IOException {
        String header = "HTTP/1.1 " + status + "\r\n" +
                        "Content-Type: " + contentType + "; charset=utf-8\r\n" +
                        "Content-Length: " + content.length + "\r\n\r\n";
        out.write(header.getBytes());
        out.write(content);
        out.flush();
    }
}

最佳实践

  • 线程池管理:避免线程频繁创建销毁,使用固定大小线程池
  • 资源关闭:使用try-with-resources确保Socket/流正确关闭
  • 响应头规范:正确设置Content-Type和Content-Length
  • 路径安全:验证文件路径防止路径遍历攻击(如检查绝对路径)
  • 异常处理:捕获IOException并记录日志,避免服务器崩溃

常见错误

  • 资源泄漏:未关闭Socket或流导致文件描述符耗尽
  • 阻塞主线程:在accept循环中直接处理请求导致无法接收新连接
  • 编码问题:响应未指定charset导致中文乱码
  • 并发瓶颈:无限制创建线程(应使用线程池)
  • 协议错误:响应头缺少空行分隔header/body,或未遵循HTTP格式

扩展知识

  • NIO非阻塞IO:使用Selector实现单线程处理多连接(java.nio包)
  • 高性能框架:Netty/Mina等网络框架处理底层细节
  • Keep-Alive:通过Connection头实现TCP连接复用
  • HTTPS支持:使用SSLServerSocket实现加密通信
  • HTTP/2:二进制分帧、头部压缩等高级特性