1. 概述
官方https://docs.oracle.com/javase/9/docs/api/com/sun/net/httpserver/package-summary.html
HttpServer 是 JDK 1.6 以後內置的一個輕量級 HTTP 服務器(在 rt.jar 包中的 com.sun.net.httpserver 包下)。
一個 HttpServer 實例被綁定到一個IP地址和端口號,並監聽來自該地址的客戶端TCP連接。
其子類 HttpsServer 實現了 HTTPS 服務,還能處理 HTTPS 請求。
一個簡單的 HTTP 服務器:
// 創建 http 服務器, 綁定本地 8080 端口 HttpServer httpServer = HttpServer.create(new InetSocketAddress(8080), 0); // 創建上下文監聽, "/" 表示匹配所有 URI 請求 httpServer.createContext("/", new HttpHandler() { @Override public void handle(HttpExchange httpExchange) throws IOException { /* * PS: 必須按順序設置響應: 添加響應頭, 發送響應碼和內容長度, 寫出響應內容, 關閉處理器 */ // 響應內容 byte[] respContents = "Hello World".getBytes("UTF-8"); // 設置響應頭 httpExchange.getResponseHeaders().add("Content-Type", "text/html; charset=UTF-8"); // 設置響應code和內容長度 httpExchange.sendResponseHeaders(200, respContents.length); // 設置響應內容 httpExchange.getResponseBody().write(respContents); // 關閉處理器, 同時將關閉請求和響應的輸入輸出流(如果還沒關閉) httpExchange.close(); } }); // 啓動服務 httpServer.start();
瀏覽器訪問: http://localhost:8080,輸出: Hello World
使用 HttpServer 實現一個 HTTP 服務器主要涉及下面幾個類:
HttpServer: 表示一個服務器實例
HttpContext: 服務器監聽器的上下文
HttpHandler: 上下文對應的 http 請求處理器
HttpExchange: 對 http 請求和響應的數據封裝
2. 服務器: HttpServer
創建 HttpServer 實例:
/** * 創建 HttpServer 實例, 參數說明: * addr: 服務綁定的地址端口 * backlog: TCP連接最大併發數, 傳 0 或負數表示使用默認值 */ HttpServer httpServer = HttpServer.create(InetSocketAddress addr, int backlog);
HttpServer 常用方法:
// 重新綁定地址和端口 void bind(InetSocketAddress addr, int backlog) // 獲取當前綁定的地址 InetSocketAddress getAddress() /** * 創建監聽的上下文, 請求 URI 根路徑的匹配, 根據不同的 URI 根路徑選擇不同的 HttpHandler 處理請求, * 路徑必須以 "/" 開頭。路徑 "/" 表示匹配所有的請求 URI(沒有其他更具體的匹配路徑除外)。 */ HttpContext createContext(String path) HttpContext createContext(String path, HttpHandler handler) // 移除上下文監聽 void removeContext(HttpContext context) void removeContext(String path) // 設置請求的線程執行器, 設置爲 null 表示使用默認的執行器 void setExecutor(Executor executor) Executor getExecutor() // 啓動服務 void start() // 最長等待指定時間後停止服務 void stop(int delay)
3. 上下文: HttpContext
HTTP 上下文,實際上就是對請求的 URI 根路徑匹配的監聽,可以創建多個 HttpContext,一個 HttpContext 對應一個 HttpHandler,不同的 URI 請求,根據添加的 HttpContext 監聽器,分配到對應的 HttpHandler 處理請求。
PS: 一個 HTTP 請求只會被一個“最匹配”的 HttpContext 處理。
創建 HTTP 上下文監聽器:
HttpServer httpServer = HttpServer.create(...); /* * 上下文監聽器對應的 URI 根路徑,必須以 "/" 開頭, * 表示以 "/xxx" 開頭的 URI 請求都交給對應的 httpHandler 處理, * "/" 表示匹配所有的請求, 一個請求只會交給 path 最匹配的一個上下文去處理(不能重複處理) */ String path = "/xxx"; // 可以創建多個,以實現更細緻的 URI 路徑匹配來分開處理來自不同 URI 路徑的請求 httpServer.createContext(path, new HttpHandler() { @Override public void handle(HttpExchange httpExchange) throws IOException { // 處理匹配當前上下文 path 的請求 } });
請求 URI 的根路徑匹配關係:
4. 處理器: HttpHandler ( HttpExchange )
HttpHandler 是 HttpContext 對應的請求處理監聽器,監聽器回調時傳入的HttpExchange對象封裝了對 http 請求和響應的所有數據操作。
HttpExchange 與 請求 相關的方法:
// 獲取請求的 URI, 請求鏈接除去協議和域名端口後的部分, 如: http://www.abc.com/aa/bb, URI 爲 /aa/bb URI getRequestURI() // 獲取請求客戶端的 IP 地址 InetSocketAddress getRemoteAddress() // 獲取請求協議, 例如: HTTP/1.1 String getProtocol() // 獲取請求的方法, "GET", "POST" 等 String getRequestMethod() // 獲取所有的請求頭 Headers getRequestHeaders() // 以輸入流的方式獲取請求內容 InputStream getRequestBody()
HttpExchange 與 響應 相關的方法:
// 獲取響應頭的 Map, 要添加頭, 獲取到 headers 後調用 add(key, value) 方法添加 Headers getResponseHeaders() // 發送響應頭, 並指定 響應code 和 響應內容的長度 void sendResponseHeaders(int rCode, long responseLength) // 獲取響應內容的輸出流, 響應內容寫到該流 OutputStream getResponseBody()
HttpExchange 其他方法:
// 關閉處理器, 同時將關閉請求和響應的輸入輸出流(如果還沒關閉) void close() // 獲取此請求對應的上下文對象 HttpContext getHttpContext() // 獲取收到請求的本地地址 InetSocketAddress getLocalAddress()
5. 代碼實例
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; /** * JDK原生HttpServer * * @author FelixZh */ public class JDKHttpServer { public static void main(String[] args) throws Exception { InetSocketAddress inetSocketAddress = new InetSocketAddress(InetAddress.getByName("localhost"), 8080); HttpServer httpServer = HttpServer.create(inetSocketAddress, 0); //監聽上下文.指定匹配url httpServer.createContext("/", new MyHttpHandler()); httpServer.createContext("/hello", new MyHttpHandler()); //啓動服務 httpServer.start(); System.out.println("Start..."); } } class MyHttpHandler implements HttpHandler { @Override public void handle(HttpExchange httpExchange) throws IOException { System.out.println(httpExchange.getRemoteAddress()); System.out.println(httpExchange.getProtocol()); System.out.println(httpExchange.getRequestMethod()); System.out.println(httpExchange.getRequestURI()); byte[] resp = "FelixZh".getBytes(StandardCharsets.UTF_8); //設置響應頭 httpExchange.getResponseHeaders().add("Content-Type", "text/html; charset=UTF-8"); //設置響應code和內容長度 httpExchange.sendResponseHeaders(200, resp.length); //設置響應內容 httpExchange.getResponseBody().write(resp); //關閉處理器 httpExchange.close(); } }