侧边栏壁纸
博主头像
colo

欲买桂花同载酒

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

使用Java实现一个简单的多线程HTTP服务器

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

题目

使用Java实现一个简单的多线程HTTP服务器

信息

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

考点

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

快速回答

实现要点:

  • 使用ServerSocket监听端口
  • 对每个连接创建独立线程处理
  • 解析HTTP请求行和头部
  • 根据请求路径返回文件内容或404响应
  • 正确关闭资源
## 解析

核心原理

HTTP服务器基于TCP协议,通过ServerSocket监听指定端口(如8080)。当客户端连接时,服务器需要:

  1. 解析HTTP请求(方法、路径、协议版本)
  2. 定位请求资源(如本地文件)
  3. 生成符合HTTP协议的响应(状态行、头部、正文)

代码示例

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

public class SimpleHttpServer {
    private static final int PORT = 8080;
    private static final String WEB_ROOT = "./www";
    private static final ExecutorService threadPool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Listening on port " + PORT);
            while (true) {
                Socket clientSocket = serverSocket.accept();
                threadPool.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".equalsIgnoreCase(method)) {
                sendResponse(out, "HTTP/1.1 405 Method Not Allowed\r\n\r\n");
                return;
            }

            // 安全处理路径
            File file = new File(WEB_ROOT, path.replace("..", "").replace("/", File.separator));
            if (file.isFile()) {
                sendFile(out, file);
            } else {
                sendResponse(out, "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\n\r\n404 Not Found");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try { clientSocket.close(); } catch (IOException ignore) {}
        }
    }

    private static void sendFile(OutputStream out, File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            String contentType = getContentType(file.getName());
            String header = "HTTP/1.1 200 OK\r\n" +
                            "Content-Type: " + contentType + "\r\n" +
                            "Content-Length: " + file.length() + "\r\n\r\n";
            out.write(header.getBytes());
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
    }

    private static void sendResponse(OutputStream out, String response) throws IOException {
        out.write(response.getBytes());
    }

    private static String getContentType(String fileName) {
        if (fileName.endsWith(".html")) return "text/html";
        if (fileName.endsWith(".txt")) return "text/plain";
        return "application/octet-stream";
    }
}

最佳实践

  • 线程池管理:使用ExecutorService避免线程频繁创建销毁
  • 资源关闭:使用try-with-resources确保Socket/Stream正确关闭
  • 路径安全:过滤 '..' 防止目录遍历攻击
  • 内容类型:根据文件扩展名设置Content-Type头部

常见错误

  • 未处理长连接:HTTP/1.1默认keep-alive,需正确处理Connection头部
  • 资源泄漏:忘记关闭Socket或流导致文件描述符耗尽
  • 阻塞主线程:未使用多线程/线程池导致无法并发处理请求
  • 编码问题:响应文本时未指定正确字符集

扩展知识

  • HTTP/1.1特性:需支持Host头部、分块传输编码(chunked)
  • 性能优化:使用NIO(Selector)实现非阻塞IO
  • 安全加固:添加超时控制、请求大小限制
  • 标准替代方案:生产环境建议使用Tomcat/Netty等成熟框架