为了账号安全,请及时绑定邮箱和手机立即绑定

HttpTunnelServer,从源码看Springboot如何实现双向数据交互

标签:
Java

Tunnel是什么?

Tunnel存在的意义,就是使用http协议来传输非http协议的内容,在使用fiddler4抓包的时候,常常会看见Tunnel这样的包,打开之后会发现里面没有标准http协议的模式,没有http头,包体也和http协议包体不相同。

Tunnel可以用来做服务器和客户端进行双向交流,这就解决了http1.1中无法实现服务器主动给客户端发送信息的问题。

Springboot中如何实现Tunnel?

Springboot中提供了HttpTunnelServer来提供Tunnel这样的功能,我们可以先来看一看它的源码

public HttpTunnelServer(TargetServerConnection serverConnection) {
    Assert.notNull(serverConnection, "ServerConnection must not be null");
    this.serverConnection = serverConnection;
}

从构造方法中我们可以看出,传入了一个TargetServerConnection实例,而TargetServerConnection是一个函数式接口。

@FunctionalInterface
public interface TargetServerConnection {
	ByteChannel open(int timeout) throws IOException;
}

在源码中,有一个该接口的实现类SocketTargetServerConnection

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自己的测试代码中

@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 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,我们就可以和远端集成服务器双向交流
>炒鸡辣鸡原创文章,转载请注明来源

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消