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);
});

相关推荐
找不到、了1 小时前
Java设计模式之《原型模式》--深、浅copy
java·设计模式·原型模式
程序员岳焱3 小时前
Java 调用 Python 脚本:实现 HelloWorld
java·后端·python
etcix3 小时前
wrap cpp variant as dll for c to use
java·c语言·开发语言
我在北国不背锅4 小时前
基于Java的Markdown转Word工具(标题、段落、表格、Echarts图等)
java·word·echarts·markdown
pengzhuofan4 小时前
Java设计模式-建造者模式
java·设计模式·建造者模式
夕四丶5 小时前
【java实现一个接口多个实现类通用策略模式】
java·策略模式
找不到、了5 小时前
Java设计模式之《策略模式》
java·设计模式·策略模式
刘火锅5 小时前
设计模式-策略模式 Java
java·设计模式·策略模式
岁忧5 小时前
(LeetCode 每日一题) 1780. 判断一个数字是否可以表示成三的幂的和 (数学、三进制数)
java·c++·算法·leetcode·职场和发展·go
一颗星的征途6 小时前
java循环分页查询数据,任何把查询到的数据,分批处理,多线程提交到数据库清洗数据
java·数据库·mysql·spring cloud