使用 Java 原生 TCP Socket 實現 HTTP 請求解析和請求分發,是一個理解 HTTP 協議底層原理的好方法。雖然 Java 提供了 HttpServer
類來簡化 HTTP 服務器開發,但如果你想從 TCP 層 開始構建一個簡單的 HTTP 服務器,可以使用 ServerSocket
和 Socket
實現。
在進行web開發前.我們擴展一下?8.3 Java HTTP-CSDN博客?的內容.實現http請求分發
使用 Java 原生 TCP Socket 實現:
- 接收 HTTP 請求(GET/POST)
- 解析請求頭和請求路徑
- 根據路徑分發請求到不同處理方法
- 返回 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);
});