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

從零手寫 Java HTTP Web Server:核心原理與多執行緒實作

     編輯:Java 相關知識

一、 HTTP 協議核心流程回顧

Web Server 本質是處理 TCP 連線、解析 HTTP 請求、回應 HTTP 響應 的程式。核心四步驟:

  1. 建立連線:ServerSocket 監聽埠口 (預設 80),Accept 獲得 Socket。
  2. 接收請求:從 Socket 輸入流讀取請求行 (如 GET /index.html HTTP/1.1) 與 Header。
  3. 處理與響應:根據路徑尋找資源 (靜態檔案/動態處理),組裝響應:
    Status-Line + Headers (Content-Type, Content-Length...) + 空行 + Body。
  4. 關閉連線:HTTP/1.0 預設短連線,回應完成即關閉 Socket。

二、 Java 實作多執行緒 Web Server 完整範例

以下程式碼示範支援 GET 請求、靜態檔案服務、多執行緒並發處理的最小化 Server:

1. 主類別:監聽與分派

import java.io.*;
import java.net.*;

public class SimpleWebServer {
    private static final int PORT = 8080;

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(PORT);
        System.out.println("Server started on port " + PORT);

        while (true) {
            Socket clientSocket = serverSocket.accept(); // 阻塞等待連線
            // 每個連線開啟一條執行緒處理
            new Thread(new HttpRequestHandler(clientSocket)).start();
        }
    }
}

2. 請求處理器:解析與響應

import java.io.*;
import java.net.Socket;
import java.nio.file.*;

class HttpRequestHandler implements Runnable {
    private final Socket socket;

    HttpRequestHandler(Socket socket) { this.socket = socket; }

    @Override
    public void run() {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             OutputStream out = socket.getOutputStream()) {

            // 1. 讀取請求行
            String requestLine = in.readLine();
            if (requestLine == null || requestLine.isEmpty()) return;

            // 解析 GET /path HTTP/1.1
            String[] parts = requestLine.split("\\s+");
            if (parts.length < 3 || !"GET".equalsIgnoreCase(parts[0])) {
                sendResponse(out, 405, "Method Not Allowed", "text/plain", "Only GET supported");
                return;
            }
            String path = parts[1].equals("/") ? "/index.html" : parts[1];

            // 2. 定位檔案 (簡易:從 classpath 或工作目錄讀取)
            Path filePath = Paths.get(".", "webroot", path).normalize();
            // 安全檢查:防止目錄遍歷攻擊
            if (!filePath.startsWith(Paths.get(".", "webroot").toAbsolutePath())) {
                sendResponse(out, 403, "Forbidden", "text/plain", "Access denied");
                return;
            }

            // 3. 讀取檔案並響應
            if (Files.exists(filePath) && Files.isRegularFile(filePath)) {
                String mimeType = guessMimeType(filePath.toString());
                byte[] content = Files.readAllBytes(filePath);
                sendResponse(out, 200, "OK", mimeType, content);
            } else {
                sendResponse(out, 404, "Not Found", "text/html", "

404 Not Found

".getBytes()); } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException ignored) {} } } // 發送完整 HTTP 響應 private void sendResponse(OutputStream out, int statusCode, String statusText, String contentType, byte[] body) throws IOException { PrintWriter writer = new PrintWriter(out, true); writer.printf("HTTP/1.1 %d %s\r\n", statusCode, statusText); writer.printf("Content-Type: %s\r\n", contentType); writer.printf("Content-Length: %d\r\n", body.length); writer.printf("Connection: close\r\n"); writer.print("\r\n"); writer.flush(); out.write(body); out.flush(); } private void sendResponse(OutputStream out, int statusCode, String statusText, String contentType, String body) throws IOException { sendResponse(out, statusCode, statusText, contentType, body.getBytes()); } // 簡易 MIME 類型判斷 private String guessMimeType(String fileName) { if (fileName.endsWith(".html") || fileName.endsWith(".htm")) return "text/html; charset=UTF-8"; if (fileName.endsWith(".css")) return "text/css"; if (fileName.endsWith(".js")) return "application/javascript"; if (fileName.endsWith(".png")) return "image/png"; if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) return "image/jpeg"; return "application/octet-stream"; } }

三、 關鍵細節與擴充方向

  • 執行緒池:生產環境請用 ExecutorService 替代 `new Thread()`,避免執行緒耗盡。
  • 非阻塞 I/O (NIO):高併發場景可升級為 Selector + SocketChannel 模型 (如 Netty)。
  • HTTP 協議完整支援:POST、Header 解析、Chunked 編碼、Keep-Alive、管線化等。
  • 動態請求處理:整合 Servlet 容器 (Tomcat/Jetty) 或自行實作 Servlet API。
  • 安全性:路徑規範化防目錄遍歷、輸入驗證、HTTPS (SSL/TLS)。

總結

手寫 Web Server 是理解網路程式設計、HTTP 協議與 Java I/O 模型的最佳練習。從阻塞式多執行緒起步,逐步引入執行緒池、NIO、協議完整解析,即可演進出生產級的高效能伺服器。

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