首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

從零構建 Java HTTP Web 服務器:多線程架構與請求處理全解析

     編輯:Java 相關知識

理解 HTTP 協議與 Socket 編程是後端開發的內功心法。本文從 HTTP 協議四步工作原理出發,逐步構建一個支持多線程併發、GET 請求處理、靜態文件服務、錯誤響應的輕量級 Web 服務器,代碼量不足 200 行卻包含服務器核心要素,是學習網絡編程、Servlet 容器原理的最佳切入點。

HTTP 協議工作流程

  1. 建立連接:瀏覽器與服務器建立 TCP 連接(默認端口 80/8080)
  2. 發送請求:瀏覽器發送 HTTP 請求報文(請求行 + 請求頭 + 請求體)
  3. 處理響應:服務器解析請求,處理業務邏輯,返回 HTTP 響應報文(狀態行 + 響應頭 + 響應體)
  4. 斷開連接:HTTP/1.0 默認短連接,完成即斷開;HTTP/1.1 支持 Keep-Alive

核心服務器實現


public class WebServer {
    public static void main(String[] args) throws IOException {
        int port = 8080;
        ServerSocket server = new ServerSocket(port);
        System.out.println("Web Server listening on port " + server.getLocalPort());

        int counter = 1;
        while (true) {
            Socket client = server.accept(); // 阻塞等待連接
            new ConnectionThread(client, counter++).start(); // 每連接一線程
        }
    }
}

class ConnectionThread extends Thread {
    private final Socket client;
    private final int id;

    public ConnectionThread(Socket client, int id) {
        this.client = client;
        this.id = id;
    }

    @Override
    public void run() {
        try {
            String clientIP = client.getInetAddress().toString();
            int clientPort = client.getPort();
            System.out.println("Connection " + id + ": " + clientIP + ":" + clientPort);

            PrintStream out = new PrintStream(client.getOutputStream());
            DataInputStream in = new DataInputStream(client.getInputStream());

            String requestLine = in.readLine(); // 讀取請求行
            System.out.println("Received: " + requestLine);

            if (isGetRequest(requestLine)) {
                String filename = getFilename(requestLine);
                File file = new File(filename);
                
                if (file.exists() && file.isFile()) {
                    serveFile(out, file);
                } else {
                    serve404(out);
                }
            }
            client.close();
        } catch (IOException e) {
            System.err.println("Connection " + id + " error: " + e.getMessage());
        }
    }

    private boolean isGetRequest(String line) {
        return line != null && line.toUpperCase().startsWith("GET ");
    }

    private String getFilename(String requestLine) {
        // GET /path/file.html HTTP/1.0
        String path = requestLine.substring(4, requestLine.indexOf(' ', 4));
        if (path.startsWith("/")) path = path.substring(1);
        if (path.isEmpty()) path = "index.html";
        return path;
    }

    private void serveFile(PrintStream out, File file) throws IOException {
        out.println("HTTP/1.0 200 OK");
        out.println("MIME_version: 1.0");
        out.println("Content_Type: text/html");
        out.println("Content_Length: " + file.length());
        out.println(); // 空行分隔頭體

        try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
            byte[] buffer = new byte[(int) file.length()];
            in.readFully(buffer);
            out.write(buffer);
            out.flush();
        }
    }

    private void serve404(PrintStream out) {
        String body = "Not Found" +
                      "

Error 404 - File Not Found

"; out.println("HTTP/1.0 404 Not Found"); out.println("Content_Type: text/html"); out.println("Content_Length: " + body.length()); out.println(); out.println(body); out.flush(); } }

關鍵技術點深度解析

1. 多線程併發模型

  • 一連接一線程:簡單直觀,適用於輕量級服務器
  • 線程池優化:生產環境應使用 `ExecutorService` 避免線程創建開銷與 OOM 風險
  • NIO 升級路徑:高併發場景可遷移至 `ServerSocketChannel` + `Selector` 事件驅動模型

2. HTTP 協議解析細節


// 標準請求行格式
GET /index.html HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0
Accept: text/html
Connection: keep-alive

// 響應格式
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
Date: Mon, 23 Jan 2023 10:00:00 GMT
Server: MiniJavaWeb/1.0

<html>...</html>

關鍵點:雙換行符(`\r\n\r\n`)分隔頭與體,`Content-Length` 必須準確,否則客戶端會掛起等待。

3. 安全性考量

  1. 路徑遍歷防護:`getFilename` 必須規範化路徑,防止 `../../etc/passwd` 攻擊
  2. MIME 類型映射:根據文件擴展名設置正確 `Content-Type`(CSS/JS/圖片等)
  3. 輸入驗證:請求行長度限制,防緩衝區溢出

擴展方向:從玩具到生產級

功能模塊實現方向參考組件
靜態資源優化緩存、壓縮、ETag、Range 請求Nginx 靜態模塊
動態請求處理Servlet 規範實現、Filter 鏈、Session 管理Tomcat Catalina
HTTPS 支持SSLContext + ServerSocketFactoryJSSE
虛擬主機Host Header 路由、多域名證書Tomcat Host/Context
監控指標QPS、延遲分位數、錯誤率、線程池狀態Micrometer + Prometheus

啟動驗證


$ javac WebServer.java
$ java WebServer
Web Server listening on port 8080
Connection 1: /127.0.0.1:54321
Received: GET /index.html HTTP/1.1

瀏覽器訪問 `https://localhost:8080/index.html` 即可看到服務效果。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved