Tunnel是什麼?
Tunnel存在的意義,就是使用http協議來傳輸非http協議的內容,在使用fiddler4抓包的時候,經常會看見Tunnel這樣的包,打開以後會發現裏面沒有標準http協議的模式,沒有http頭,包體也和http協議包體不相同。java
Tunnel能夠用來作服務器和客戶端進行雙向交流,這就解決了http1.1中沒法實現服務器主動給客戶端發送信息的問題。web
Springboot中如何實現Tunnel?
Springboot中提供了HttpTunnelServer來提供Tunnel這樣的功能,咱們能夠先來看一看它的源碼服務器
public HttpTunnelServer(TargetServerConnection serverConnection) { Assert.notNull(serverConnection, "ServerConnection must not be null"); this.serverConnection = serverConnection; }
從構造方法中咱們能夠看出,傳入了一個TargetServerConnection實例,而TargetServerConnection是一個函數式接口。app
@FunctionalInterface public interface TargetServerConnection { ByteChannel open(int timeout) throws IOException; }
在源碼中,有一個該接口的實現類SocketTargetServerConnectionsocket
public class SocketTargetServerConnection implements TargetServerConnection { public SocketTargetServerConnection(PortProvider portProvider) { Assert.notNull(portProvider, "PortProvider must not be null"); this.portProvider = portProvider; } @Override public ByteChannel open(int socketTimeout) throws IOException { SocketAddress address = new InetSocketAddress(this.portProvider.getPort()); logger.trace(LogMessage.format("Opening tunnel connection to target server on %s", address)); SocketChannel channel = SocketChannel.open(address); channel.socket().setSoTimeout(socketTimeout); return new TimeoutAwareChannel(channel); } ... ... } // SocketTargetServerConnection的構造函數傳入的是函數式接口PortProvider, // 是一個獲取port的函數式接口 @FunctionalInterface public interface PortProvider { /** * Return the port number. * @return the port number */ int getPort(); }
很明顯,這個方法是提供的該服務於其餘服務進行Tunnel交互的,SocketTargetServerConnection的open函數是打開一個端口與遠程端口進行交互。在Springboot本身的測試代碼中ide
@Bean DispatcherFilter filter(AnnotationConfigServletWebServerApplicationContext context) { TargetServerConnection connection = new SocketTargetServerConnection( () -> context.getWebServer().getPort()); HttpTunnelServer server = new HttpTunnelServer(connection); // 在webServer的對等端口,定義了一個HttpTunnelServer HandlerMapper mapper = new UrlHandlerMapper("/httptunnel", new HttpTunnelServerHandler(server)); // 建立一個處理器 Collection<HandlerMapper> mappers = Collections.singleton(mapper); // 建立單例 Dispatcher dispatcher = new Dispatcher(AccessManager.PERMIT_ALL, mappers); // 建立處理任務分發 return new DispatcherFilter(dispatcher); // 監聽並分發處理任務 }
// HttpTunnelServerHandler.java public class HttpTunnelServerHandler implements Handler { private HttpTunnelServer server; public HttpTunnelServerHandler(HttpTunnelServer server) { Assert.notNull(server, "Server must not be null"); this.server = server; } @Override public void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException { this.server.handle(request, response); } } // 其中Handler也是一個函數式接口 @FunctionalInterface public interface Handler { /** * Handle the request. * @param request the request * @param response the response * @throws IOException in case of I/O errors */ void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException; }
也就是說,在須要與遠程進行Tunnel隧道傳輸的時候,HttpTunnelServer經過將數據包封裝進Servlet中的方法,首先與遠端服務器創建connet鏈接,鏈接創建成功之後,將數據包再經過Http的方法傳出去。函數
有了HttpTunnelServer,咱們就能夠和遠端集成服務器雙向交流測試
炒雞辣雞原創文章,轉載請註明來源this