點一下關注吧!!!非常感謝!!持續更新!!!
🚀 AI篇持續更新中!(長期更新)
目前2025年06月13日更新到:
AI煉丹日志-28 - Audiblez 將你的電子書epub轉換為音頻mp3 做有聲書,持續打造實用AI工具指南!📐🤖
💻 Java篇正式開啟!(300篇)
目前2025年06月11日更新到:
Java-44 深入淺出 Nginx - 底層進程機制 Master Worker 機制原理 常用指令
MyBatis 已完結,Spring 已完結,深入淺出助你打牢基礎!
📊 大數據板塊已完成多項干貨更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余項核心組件,覆蓋離線+實時數倉全棧!
目前2025年06月13日更新到:
大數據-278 Spark MLib - 基礎介紹 機器學習算法 梯度提升樹 GBDT案例 詳解
MiniCat
基本介紹
我們要手寫實現一個 Mini 版的 Tomcat,我們要實現的是,作為一個服務器軟件,可以通過我們的瀏覽器客戶端發送HTTP請求,Minicat處理之后,處理結果可以返回給瀏覽器的客戶端。
● 提供服務,接受請求(Socket通信)
● 請求信息封裝成 Request 對象(Response對象)
● 客戶端請求資源,資源分為靜態資源(HTML)和動態資源(Servlet)
● 資源返回客戶端瀏覽器
HttpProtocoUtil 協議類
public class HttpProtocoUtil {public static String getHttpHeader200(long contentLength) {return "HTTP/1.1 200 OK \n" +"Content-Type: text/html \n" +"Content-Length: " + contentLength + " \n" +"\r\n";}public static String getHttpHeader404() {String str404 = "<h1>404 not found</h1>";return "HTTP/1.1 404 NOT Found \n" +"Content-Type: text/html \n" +"Content-Length: " + str404.getBytes().length + " \n" +"\r\n" + str404;}
}
HttpProtocoUtil,它封裝了兩個方法,用于手動構造 HTTP 響應報文字符串,分別用于:
- 返回 HTTP 200 成功響應的報文頭;
- 返回 HTTP 404 未找到的完整響應報文(包含 header + body)。
關鍵點分析:
- str404: 定義了一個 HTML 格式的正文
404 not found
- str404.getBytes().length: 計算正文的字節數,填入 Content-Length 字段中
- 最后 + str404: 將正文內容添加在頭部之后
HttpServlet 基礎類
public abstract class HttpServlet implements Servlet {public abstract void doGet(Request request,Response response);public abstract void doPost(Request request,Response response);@Overridepublic void service(Request request, Response response) throws Exception {if("GET".equalsIgnoreCase(request.getMethod())) {doGet(request,response);}else{doPost(request,response);}}
}
簡化版的 HttpServlet 類,是你手寫 Web 容器(例如 MiniCat)中模仿 Java EE javax.servlet.http.HttpServlet 的核心組件。
- abstract class: 抽象類,不能直接被實例化,必須由子類實現其抽象方法。
- implements Servlet: 表示這個類實現了一個自定義的 Servlet 接口(你可能已經自己定義了 Servlet 接口),這與 Java Web 中的標準接口 javax.servlet.Servlet 類似。
在抽象方法中:
-
聲明了兩個抽象方法,分別用于處理 GET 請求 和 POST 請求。
-
子類必須實現它們,否則編譯報錯。
Request request: 封裝了 HTTP 請求的信息(如 URI、參數、方法類型等)。
- Response response: 封裝了 HTTP 響應的信息(如設置狀態碼、響應內容等)。
這兩個類也應該是你自己定義的簡化版 Request 和 Response 類,用于模擬真實的 Servlet API 行為。
而在service(Request, Response)中:
-
實現了 Servlet 接口中的 service() 方法,作為請求處理的統一入口。
-
根據 request.getMethod() 返回的 HTTP 方法(如 “GET” 或 “POST”),分發給 doGet() 或 doPost() 方法。
-
equalsIgnoreCase: 忽略大小寫比較,兼容 “get”, “GET” 等寫法。
-
request.getMethod(): 獲取請求方法字符串,應該是你自定義 Request 類中的一個方法。
-
throws Exception: 表明此方法可能拋出異常,讓調用方去處理,便于靈活控制異常響應。
假設我們有一個具體的實現類:
public class HelloServlet extends HttpServlet {@Overridepublic void doGet(Request req, Response resp) {resp.write("Hello, GET!");}@Overridepublic void doPost(Request req, Response resp) {resp.write("Hello, POST!");}
}
當用戶訪問你的 Web 服務 /hello 時:
- 如果是 GET 請求,會調用 doGet(),返回 “Hello, GET!”。
- 如果是 POST 請求,會調用 doPost(),返回 “Hello, POST!”。
Request 請求處理類
public class Request {private String method;private String url;private InputStream inputStream;public Request() {}public Request(InputStream inputStream) throws IOException {this.inputStream = inputStream;int count = 0;while (count == 0) {count = inputStream.available();}byte[] bytes = new byte[count];inputStream.read(bytes);String inputStr = new String(bytes);// 請求頭String firstLineStr = inputStr.split("\\n")[0];String[] strings = firstLineStr.split(" ");this.method = strings[0];this.url = strings[1];System.out.println("Request method: " + method + ", url: " + url);}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public InputStream getInputStream() {return inputStream;}public void setInputStream(InputStream inputStream) {this.inputStream = inputStream;}
}
在類屬性中:
- method:HTTP 請求方法(如 GET, POST)
- url:請求的資源路徑(如 /index.html)
- inputStream:從客戶端 socket 獲取的原始請求數據流
在 inputStream.available() 循環讀取,目的是等到客戶端數據準備好再讀取。
while (count == 0) {count = inputStream.available();
}
RequestProcessor 響應類
public class RequestProcessor extends Thread {private Socket socket;private Map<String, HttpServlet> servletMap;public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {this.socket = socket;this.servletMap = servletMap;}@Overridepublic void run() {try {InputStream inputStream = socket.getInputStream();Request request = new Request(inputStream);Response response = new Response(socket.getOutputStream());// 靜態資源if (servletMap.get(request.getUrl()) == null) {response.outputHtml(request.getUrl());} else {HttpServlet servlet = servletMap.get(request.getUrl());servlet.service(request, response);}socket.close();} catch (Exception e) {e.printStackTrace();}}
}
RequestProcessor 這是一個繼承了 Thread 的類,意味著每次請求會以獨立線程的方式進行處理。它負責解析 HTTP 請求、查找對應的 Servlet 處理器,或返回靜態資源。在簡易 Web Server 中,每當 ServerSocket.accept() 接收到一個連接,就會創建這個類的一個實例,并調用 start() 進入 run() 方法處理。
此外,在RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap)構造函數中,將 socket 和 servlet 映射傳入,以便在 run() 方法中使用。這也是多線程處理請求的基礎結構:每個線程獨立處理一個連接,不共享 socket。
在核心處理邏輯中 run():從 socket 獲取輸入流、輸出流,構造出 Request 和 Response 對象。Request 負責解析 HTTP 請求頭;Response 封裝輸出功能(例如寫 HTML 響應)。
另外,在請求分發邏輯中,“servletMap.get(request.getUrl())”:
- 靜態資源路徑(如 /index.html):不在 servletMap 中,調用 response.outputHtml(),你很可能實現了這個方法來讀取本地文件并寫入 socket。
- 動態 Servlet 路徑(如 /hello):在 servletMap 中,調用 servlet.service(),執行對應的業務邏輯(最終調用 doGet() 或 doPost())。