標準 URL 協議(HTTP、FTP、FILE 等)滿足大多數場景,但在特定業務中(如內部 RPC、專有數據傳輸),我們需要定義自己的協議標識符(如 oschina://、echo://)。直接使用未註冊的協議會拋出 MalformedURLException: unknown protocol。Java 的 java.net.URL 提供了成熟的協議處理框架,通過擴展 URLStreamHandlerFactory、URLStreamHandler、URLConnection 即可實現自定義協議。
URLStreamHandler。URLConnection。oschina:// 協議public class EchoURLStreamHandlerFactory implements URLStreamHandlerFactory {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
if ("echo".equals(protocol) || "oschina".equals(protocol)) {
return new EchoURLStreamHandler();
}
return null;
}
}
public class EchoURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new EchoURLConnection(u);
}
}
此處演示基於 Socket 手動構造 HTTP 請求,實際可替換為 WebSocket、SMTP 等任意協議。
public class EchoURLConnection extends URLConnection {
private Socket connection;
private static final int DEFAULT_PORT = 80;
public EchoURLConnection(URL url) { super(url); }
@Override public synchronized InputStream getInputStream() throws IOException {
if (!connected) connect();
return connection.getInputStream();
}
@Override public synchronized OutputStream getOutputStream() throws IOException {
if (!connected) connect();
return connection.getOutputStream();
}
@Override public String getContentType() { return "text/plain"; }
@Override public synchronized void connect() throws IOException {
if (connected) return;
int port = url.getPort();
if (port < 0 || port > 65535) port = DEFAULT_PORT;
connection = new Socket(url.getHost(), port);
// Socket 參數優化
connection.setTcpNoDelay(true);
connection.setSoTimeout(30000);
connection.setKeepAlive(true);
// ... 其他 Socket 設置省略
// 手動構造 HTTP POST 請求
StringBuilder req = new StringBuilder();
req.append("POST ").append(url.getPath()).append(" HTTP/1.1\r\n");
req.append("Host: ").append(url.getHost());
if (port != 80) req.append(":").append(port);
req.append("\r\nConnection: keep-alive\r\n");
req.append("Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n");
String body = "name=zhangsan&password=123456";
req.append("Content-Length: ").append(body.getBytes("UTF-8").length).append("\r\n\r\n");
req.append(body);
connection.getOutputStream().write(req.toString().getBytes("UTF-8"));
connected = true;
}
@Override public synchronized void disconnect() throws IOException {
if (connected) { connection.close(); connected = false; }
}
}
public class TestCustomProtocol {
public static void main(String[] args) throws Exception {
URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory());
URL url = new URL("oschina://localhost:8080/test/ios.php");
EchoURLConnection conn = (EchoURLConnection) url.openConnection();
conn.setDoOutput(true); conn.setDoInput(true);
// 發送數據、讀取響應...
// 處理 Chunked 編碼響應...
}
}
實現 ContentHandler 並註冊 URLConnection.setContentHandlerFactory(),可自定義響應內容的解析邏輯(如直接返回首行文本)。
Java URL 框架提供了標準化的協議擴展機制,核心在於實現三大 SPI 接口。自定義協議本質上是對 I/O 規範的封裝,靈活性極高,適用於私有協議、內部 RPC 等場景。