手写tomcat

手写一个简易版的 Tomcat(迷你 Web 服务器),核心是理解 Tomcat 的底层原理:基于 Socket 监听 HTTP 请求、解析请求报文、处理请求并返回 HTTP 响应。下面我会分步骤拆解实现思路,并提供可运行的核心代码,帮你从 0 到 1 实现一个最小可用的迷你 Tomcat。

一、核心原理梳理

Tomcat 本质是一个「HTTP 协议的 Socket 服务器」,核心能力包括:

  1. 监听指定端口(如 8080),等待客户端(浏览器)连接;
  2. 接收并解析 HTTP 请求报文(请求行、请求头、请求体);
  3. 根据请求路径(如 /hello)匹配对应的处理逻辑(Servlet 思想);
  4. 构造 HTTP 响应报文,返回给客户端。

二、手写迷你 Tomcat 步骤(附代码)

步骤 1:定义核心接口(模拟 Servlet 规范)

Servlet 是 Tomcat 处理请求的核心组件,我们先定义一个极简的 Servlet 接口,约定处理请求的规范:

java

运行

复制代码
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

// 模拟 Servlet 接口,定义处理请求的标准
public interface Servlet {
    // 初始化方法(Tomcat 启动时执行)
    void init();
    
    // 处理请求的核心方法
    void service(InputStream inputStream, OutputStream outputStream) throws IOException;
    
    // 销毁方法(Tomcat 关闭时执行)
    void destroy();
}
步骤 2:实现具体的 Servlet(业务处理逻辑)

编写一个具体的 HelloServlet,处理 /hello 路径的请求:

java

运行

复制代码
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

// 具体的业务处理类,对应 /hello 请求
public class HelloServlet implements Servlet {
    @Override
    public void init() {
        // 初始化逻辑(如加载配置、连接数据库)
        System.out.println("HelloServlet 初始化完成");
    }

    @Override
    public void service(InputStream inputStream, OutputStream outputStream) throws IOException {
        // 1. 构造 HTTP 响应头(必须符合 HTTP 协议规范)
        String responseHeader = "HTTP/1.1 200 OK\r\n" +
                                "Content-Type: text/html;charset=UTF-8\r\n" +
                                "\r\n"; // 空行分隔响应头和响应体
        
        // 2. 构造响应体(返回给浏览器的内容)
        String responseBody = "<html><head><title>迷你Tomcat</title></head>" +
                              "<body><h1>Hello Mini Tomcat!</h1><p>请求处理成功</p></body></html>";
        
        // 3. 拼接响应报文并返回
        String response = responseHeader + responseBody;
        outputStream.write(response.getBytes("UTF-8"));
        outputStream.flush();
    }

    @Override
    public void destroy() {
        // 销毁逻辑(如释放资源)
        System.out.println("HelloServlet 销毁");
    }
}
步骤 3:实现核心的 Tomcat 服务器(Socket 监听 + 请求分发)

这是最核心的部分,负责监听端口、解析请求、匹配 Servlet 并处理:

java

运行

复制代码
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class MiniTomcat {
    // 1. 定义端口(默认 8080)
    private int port = 8080;
    // 2. 存储 路径 -> Servlet 的映射(模拟 web.xml 配置)
    private Map<String, Servlet> servletMapping = new HashMap<>();

    // 初始化 Servlet 映射(模拟 Tomcat 加载 web.xml)
    public void initServletMapping() {
        // 注册 /hello 路径对应的 HelloServlet
        HelloServlet helloServlet = new HelloServlet();
        helloServlet.init(); // 初始化 Servlet
        servletMapping.put("/hello", helloServlet);
    }

    // 启动 Tomcat 核心方法
    public void start() {
        // 1. 初始化 Servlet 映射
        initServletMapping();
        
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("===== 迷你 Tomcat 启动成功,监听端口:" + port + " =====");
            
            // 2. 无限循环监听客户端连接(核心:Socket 阻塞等待)
            while (true) {
                // 等待浏览器连接,accept() 是阻塞方法
                Socket socket = serverSocket.accept();
                
                // 3. 处理请求(每个请求用独立线程,避免阻塞)
                new Thread(() -> handleRequest(socket)).start();
            }
        } catch (IOException e) {
            System.err.println("Tomcat 启动失败:" + e.getMessage());
        }
    }

    // 处理单个 HTTP 请求
    private void handleRequest(Socket socket) {
        try (
            InputStream inputStream = socket.getInputStream();
            // 输出流用于返回响应,这里省略声明,直接用 socket.getOutputStream()
        ) {
            // 1. 解析 HTTP 请求行,获取请求路径(极简版解析,仅处理 GET 请求)
            String requestPath = parseRequestPath(inputStream);
            System.out.println("收到请求:" + requestPath);
            
            // 2. 根据请求路径匹配 Servlet
            Servlet servlet = servletMapping.get(requestPath);
            if (servlet != null) {
                // 3. 调用 Servlet 的 service 方法处理请求
                servlet.service(inputStream, socket.getOutputStream());
            } else {
                // 4. 未匹配到 Servlet,返回 404 响应
                String notFoundResponse = "HTTP/1.1 404 Not Found\r\n" +
                                          "Content-Type: text/html;charset=UTF-8\r\n" +
                                          "\r\n" +
                                          "<h1>404 - 页面不存在</h1>";
                socket.getOutputStream().write(notFoundResponse.getBytes("UTF-8"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close(); // 关闭连接
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 极简版 HTTP 请求解析:仅提取请求行中的路径(如 GET /hello HTTP/1.1 → /hello)
    private String parseRequestPath(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = inputStream.read(buffer);
        if (len <= 0) return "/";
        
        String requestContent = new String(buffer, 0, len);
        // 按换行拆分请求行(第一行是请求行)
        String[] lines = requestContent.split("\r\n");
        if (lines.length == 0) return "/";
        
        // 拆分请求行:GET /hello HTTP/1.1 → ["GET", "/hello", "HTTP/1.1"]
        String[] requestLine = lines[0].split(" ");
        return requestLine.length >= 2 ? requestLine[1] : "/";
    }

    // 主方法启动 Tomcat
    public static void main(String[] args) {
        new MiniTomcat().start();
    }
}
步骤 4:测试运行
  1. 把上述 3 个类放在同一个 Java 项目中;
  2. 运行 MiniTomcatmain 方法,控制台会输出:===== 迷你 Tomcat 启动成功,监听端口:8080 =====
  3. 打开浏览器,访问 http://localhost:8080/hello,会看到「Hello Mini Tomcat!」的页面;
  4. 访问 http://localhost:8080/xxx(不存在的路径),会看到 404 页面。

三、进阶扩展(可选,贴近真实 Tomcat)

上述是最小可用版,真实 Tomcat 还包含更多核心能力,可逐步扩展:

  1. 完善 HTTP 解析:解析请求头(如 Content-Type)、请求体(POST 参数);
  2. Servlet 生命周期管理:通过反射动态加载 Servlet(而非硬编码);
  3. 上下文(Context)管理:支持多 Web 应用(多上下文);
  4. 线程池:用线程池替代单个线程,提高并发处理能力;
  5. 静态资源处理:支持返回 HTML/CSS/JS 等静态文件;
  6. 异常处理:统一处理 Servlet 执行异常,返回 500 响应。

总结

手写迷你 Tomcat 的核心关键点:

  1. 底层基石 :基于 ServerSocket 监听端口,Socket 处理客户端连接,这是 Tomcat 作为服务器的基础;
  2. 协议解析:遵循 HTTP 协议规范解析请求报文、构造响应报文(必须包含状态行、响应头、空行、响应体);
  3. 核心思想:通过「路径 - Servlet 映射」实现请求分发,体现 Tomcat 的 Servlet 容器核心逻辑。
相关推荐
寻见9032 小时前
救命!Spring Boot 凭什么火?从道法术器讲透,新手也能一键上手
java·spring boot·java ee
jinanmichael2 小时前
【SQL】掌握SQL查询技巧:数据分组与排序
java·jvm·sql
彭于晏Yan2 小时前
SpringBoot如何调用节假日API
java·spring boot·后端
jianfeng_zhu2 小时前
用java解决空心金字塔的问题
java·开发语言·python
寻见9032 小时前
告别只会 CRUD!Spring 核心原理吃透,这一篇就够了(Java 程序员必藏)
java·后端·spring
Moe4882 小时前
基于 AOP 与 Redisson 的分布式锁实现:自动加锁、解锁与 SpEL 参数解析
java·后端·架构
敲代码的嘎仔2 小时前
Java后端开发——Redis面试题汇总
java·开发语言·redis·学习·缓存·面试·职场和发展
啦啦啦_99992 小时前
4. AI面试题之 Prompt
java·prompt
YmaxU2 小时前
SpringAIAlibaba学习使用 ---核心API、RAG、Tool Calling
java·学习·spring·ai