8.4 Java 原生 TCP Socket 实现 HTTP 请求解析和请求分发

使用 Java 原生 TCP Socket 实现 HTTP 请求解析和请求分发,是一个理解 HTTP 协议底层原理的好方法。虽然 Java 提供了 HttpServer 类来简化 HTTP 服务器开发,但如果你想从 TCP 层 开始构建一个简单的 HTTP 服务器,可以使用 ServerSocketSocket 实现。

在进行web开发前.我们扩展一下 8.3 Java HTTP-CSDN博客 的内容.实现http请求分发


使用 Java 原生 TCP Socket 实现:

  1. 接收 HTTP 请求(GET/POST)
  2. 解析请求头和请求路径
  3. 根据路径分发请求到不同处理方法
  4. 返回 HTTP 响应

实现代码

1. 引入基础类

复制代码
import java.io.*;
import java.net.*;
import java.util.*;

2. 定义处理接口

复制代码
@FunctionalInterface
interface HttpRequestHandler {
    void handle(HttpRequest request, HttpResponse response) throws IOException;
}

3. 定义请求和响应封装类

复制代码
class HttpRequest {
    String method;
    String path;
    Map<String, String> headers = new HashMap<>();
    String body;

    public HttpRequest(String method, String path, Map<String, String> headers, String body) {
        this.method = method;
        this.path = path;
        this.headers = headers;
        this.body = body;
    }
}
复制代码
复制代码
class HttpResponse {
    private BufferedWriter out;
    private OutputStream rawOut;

    public HttpResponse(BufferedWriter out, OutputStream rawOut) {
        this.out = out;
        this.rawOut = rawOut;
    }

    public void send(int statusCode, String contentType, String responseBody) throws IOException {
        String statusLine = statusCode == 200 ? "HTTP/1.1 200 OK" : "HTTP/1.1 404 Not Found";
        String response =
                statusLine + "\r\n" +
                "Content-Type: " + contentType + "\r\n" +
                "Content-Length: " + responseBody.length() + "\r\n" +
                "Connection: close\r\n" +
                "\r\n" +
                responseBody;

        out.write(response);
        out.flush();
    }

    public void sendFile(int statusCode, String contentType, byte[] fileBytes) throws IOException {
        String statusLine = statusCode == 200 ? "HTTP/1.1 200 OK" : "HTTP/1.1 404 Not Found";
        String header =
                statusLine + "\r\n" +
                "Content-Type: " + contentType + "\r\n" +
                "Content-Length: " + fileBytes.length + "\r\n" +
                "Connection: close\r\n" +
                "\r\n";

        rawOut.write(header.getBytes());
        rawOut.write(fileBytes);
        rawOut.flush();
    }
}

4. 定义主服务器类

复制代码
public class SimpleHttpServerUsingTCP {
    private static final int PORT = 8080;
    private static Map<String, HttpRequestHandler> routeMap = new HashMap<>();

    public static void main(String[] args) throws IOException {
        routeMap.put("/hello", (req, res) -> {
            try {
                res.send(200, "text/plain", "Hello, World!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        routeMap.put("/about", (req, res) -> {
            try {
                res.send(200, "text/plain", "This is a simple HTTP server using TCP.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        ServerSocket serverSocket = new ServerSocket(PORT);
        System.out.println("Server started on port " + PORT);

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

    private static void handleClient(Socket socket) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        OutputStream rawOut = socket.getOutputStream();

        String line = in.readLine();
        if (line == null || line.isEmpty()) return;

        System.out.println("Request: " + line);

        // 解析请求行
        String[] requestLine = line.split(" ");
        String method = requestLine[0];
        String path = requestLine[1];

        // 解析请求头
        Map<String, String> headers = new HashMap<>();
        while (!(line = in.readLine()).isEmpty()) {
            String[] header = line.split(": ");
            if (header.length == 2) {
                headers.put(header[0], header[1]);
            }
        }

        // 读取请求体(仅处理 POST)
        int contentLength = 0;
        if (headers.containsKey("Content-Length")) {
            contentLength = Integer.parseInt(headers.get("Content-Length"));
        }

        char[] bodyBuffer = new char[contentLength];
        in.read(bodyBuffer, 0, contentLength);
        String body = new String(bodyBuffer);

        // 构建请求和响应对象
        HttpRequest request = new HttpRequest(method, path, headers, body);
        HttpResponse response = new HttpResponse(out, rawOut);

        // 路由分发
        HttpRequestHandler handler = routeMap.get(path);
        if (handler != null) {
            handler.handle(request, response);
        } else {
            response.send(404, "text/plain", "404 Not Found");
        }

        socket.close();
    }
}

测试方式

使用浏览器访问:

复制代码
http://localhost:8080/hello
http://localhost:8080/about
http://localhost:8080/unknown

使用 curl 测试:

复制代码
curl http://localhost:8080/hello
# 输出: Hello, World!

curl http://localhost:8080/about
# 输出: This is a simple HTTP server using TCP.

curl http://localhost:8080/unknown
# 输出: 404 Not Found

支持 POST 请求(可选扩展)

你可以通过解析请求体,支持 POST 请求,例如:

复制代码
routeMap.put("/post", (req, res) -> {
    System.out.println("Received POST body: " + req.body);
    res.send(200, "text/plain", "POST received: " + req.body);
});

相关推荐
好家伙VCC2 分钟前
Web Components主题热切换方案揭秘
java·前端
慕木沐11 分钟前
Google ADK Java 1.0版本 核心机制与实战 Demo
java·开发语言·python
焦虑的说说1 小时前
秒杀系统设计方案
java
许彰午1 小时前
30_Java Stream流操作全解
java·windows·python
qq_2518364572 小时前
基于java Web网络订餐系统设计与实现 源码文档
java·开发语言·前端
凡人叶枫2 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
飞天狗1112 小时前
零基础JavaWeb入门——第2课:让网页“活”起来 —— JSP是什么?
java·开发语言·前端·后端·web
梦@_@境3 小时前
面向 Spring Boot 的可观测业务流程编排引擎
java·spring boot·后端
云烟成雨TD3 小时前
Spring AI Alibaba 1.x 系列【77】执行取消
java·人工智能·spring
醇氧3 小时前
【Linux】Java 服务生产级部署指南:实现常驻后台、开机自启与系统服务化管理
java·开发语言