HttpServer:JDK 內置的輕量級 HTTP 服務器

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();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章