手写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 容器核心逻辑。
相关推荐
鱼鳞_5 分钟前
苍穹外卖-Day01(开发环境搭建)
java·spring boot·spring·maven
TAN-90°-24 分钟前
Java 6——成员变量初始值 object equals和== toString instanceof 参数传递问题
java·开发语言
中新传媒26 分钟前
德宸堂心理双师同诊
java·前端·数据库
想唱rap32 分钟前
NAT、内网穿透、代理服务
java·linux·网络·网络协议·udp·智能路由器
环流_40 分钟前
nacos环境隔离
java·服务器·前端
芋只因41 分钟前
天机学堂学习笔记
java·笔记·学习
摇滚侠1 小时前
Spring 面试题 真正的 offer 偏方 Java 基础 Java 高级
java·后端·spring
凯瑟琳.奥古斯特1 小时前
IP组播跨子网传输核心技术解析
java·开发语言·网络·网络协议·职场和发展
若水不如远方1 小时前
Java JSON 序列化原理与实战问题总结
java
hexu_blog1 小时前
前端vue后端java+springboot如何实现pdf,word,excel之间的相互转换
java·前端·vue.js·spring boot·文档转换