这篇文章主要讲解了“proxy代理示例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“proxy代理示例分析”吧!
准备工作:
将代码全部整理clone到本地,IDE可以正常打开即可。 后边的分析也都一样。
核心分析
proxy的位置
框架图
此示例主要是进行的代理访问的数据转发。
-
请求-》LocalServer=》RemoteServer
-
RemoteServer 返回数据=>LocalServer=>客户接收
代码分析
HexDumpProxy 主逻辑
代码预览:
我把原来代码中端口和域名修改了下,不影响观看
代码解读
//准备好两个EventLoop,一个是为连接使用, 一个是为读写具体数据准备 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) //管道类型 NioServerSocketChannel 装配管道类型 ,可以先不看 .handler(new LoggingHandler(LogLevel.INFO)) //(可选)此处是额外增加了一个日志打印,观看数据是否准确。 .childHandler(new HexDumpProxyInitializer(REMOTE_HOST, REMOTE_PORT))// (关键 1) 设置Child的Handler处理类 .childOption(ChannelOption.AUTO_READ, false) //(关键 2)设置自动读取为false .bind(LOCAL_PORT).sync().channel().closeFuture().sync(); //启动服务 } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
-
关键 1
HexDumpProxyInitializer 是接下来的数据读取处理Initializer初始化对象
-
关键 2
ChannelOption.AUTO_READ 将管道自动读设置成关闭,让程序后面逻辑去控制
-
LoggingHandler 日志打印
不是必要的,是为了调试看方便使用 如图
接着看 HexDumpProxyInitializer 逻辑
import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; public class HexDumpProxyInitializer extends ChannelInitializer<SocketChannel> {//继承 ChannelInitializer<SocketChannel> private final String remoteHost; private final int remotePort; public HexDumpProxyInitializer(String remoteHost, int remotePort) { //(关键 1) 将远端的地址和端口传入进来 this.remoteHost = remoteHost; this.remotePort = remotePort; } @Override public void initChannel(SocketChannel ch) { //(关键 2) 覆盖ChannelInitializer<SocketChannel>中的initChannel方法自定义初始化管道要做的事情 ch.pipeline().addLast( new LoggingHandler(LogLevel.INFO),//增加日志 new HexDumpProxyFrontendHandler(remoteHost, remotePort)); // (关键3)增加一个HexDumpProxyFrontendHandler处理器 } }
-
关键 1
将远端的地址和端口传入进来,供后面使用
-
关键 2
覆盖initChannel ,自定义初始化管道要做的内容
-
关键 3
增加一个处理器 HexDumpProxyFrontendHandler
接着看 HexDumpProxyFrontendHandler 逻辑
public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter { //(关键1) 继承ChannelInboundHandlerAdapter 输入适配器 private final String remoteHost; private final int remotePort; private Channel outboundChannel;// 声明一个输出管道 public HexDumpProxyFrontendHandler(String remoteHost, int remotePort) { this.remoteHost = remoteHost; this.remotePort = remotePort; } @Override public void channelActive(ChannelHandlerContext ctx) { //(关键2)覆盖管道激活时候触发内容 final Channel inboundChannel = ctx.channel(); //获取输入管道 //进行装配,如果管道激活,说明有请求来了,去请求后端 Bootstrap b = new Bootstrap(); b.group(inboundChannel.eventLoop())//设置和inboundChannel同样的EventLoop .channel(ctx.channel().getClass()) //设置和inboundChannel同样的Channel .handler(new HexDumpProxyBackendHandler(inboundChannel))//(关键 3) 新建一个HexDumpProxyBackendHandler 将输入管道传进去 .option(ChannelOption.AUTO_READ, false);//也是同样关闭 ChannelFuture f = b.connect(remoteHost, remotePort); //去连接远程remoteHost outboundChannel = f.channel();//获取与远程服务器建立的管道 f.addListener(new ChannelFutureListener() {// 增加一个监听当数据完成时候出发管道的状态 @Override public void operationComplete(ChannelFuture future) { if (future.isSuccess()) {//如果管道建立成功就让输入管道等待读取客户传来的信息 // connection complete start to read first data inboundChannel.read(); } else { // Close the connection if the connection attempt has failed. inboundChannel.close();//如果失败那就关闭输入流 } } }); } @Override public void channelRead(final ChannelHandlerContext ctx, Object msg) { //输入管道可以读取时候 if (outboundChannel.isActive()) { //查看远端管道是否为激活状态 outboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() { //(关键 4)将数据转发写入到outboundChannel的管道里面 @Override public void operationComplete(ChannelFuture future) { if (future.isSuccess()) { // 如果转发完成,继续读取 ctx.channel().read(); } else { future.channel().close();//否则关闭 } } }); } } @Override public void channelInactive(ChannelHandlerContext ctx) { //如果输入管道关闭,且远程管道也没有关闭的情况,进行刷新关闭。 if (outboundChannel != null) { closeOnFlush(outboundChannel); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { //如果异常时候也进行刷新关闭 cause.printStackTrace(); closeOnFlush(ctx.channel()); } /** * 静态方法,主动刷新管道数据关闭管道 */ static void closeOnFlush(Channel ch) { if (ch.isActive()) { ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); } } }
-
关键 1
继承ChannelInboundHandlerAdapter 输入适配器 可以选配覆盖里面的很多方法,自由度很高,想处理激活状态或者其他状态覆盖即可自定义了
-
关键 2
管道激活时候触发内容,在此时建立远端连接,准备好远端管道
-
关键 3
新建一个HexDumpProxyBackendHandler 将当前输入管道传进去
-
关键 4 (核心)
将数据转发写入到outboundChannel的管道里面,这是将客户过来的数据写入到远程管道里面
接着看 HexDumpProxyBackendHandler 逻辑
public class HexDumpProxyBackendHandler extends ChannelInboundHandlerAdapter { // 同样继承ChannelInboundHandlerAdapter 输入适配器 private final Channel inboundChannel; //(关键 1) 保存传入的输入管道 public HexDumpProxyBackendHandler(Channel inboundChannel) { this.inboundChannel = inboundChannel; } @Override public void channelActive(ChannelHandlerContext ctx) { //如果管道被激活,发起读请求等待 ctx.read(); } @Override public void channelRead(final ChannelHandlerContext ctx, Object msg) { inboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() { //(关键2 )当远端管道数据准备好,就可以转发到输入管道中 @Override public void operationComplete(ChannelFuture future) { if (future.isSuccess()) { ctx.channel().read(); //继续读请求 } else { future.channel().close();//如果不成功关闭管道 } } }); } @Override public void channelInactive(ChannelHandlerContext ctx) { HexDumpProxyFrontendHandler.closeOnFlush(inboundChannel); //如果远端流管道失效,调用HexDumpProxyFrontendHandler的closeOnFlush关闭输入管道,静态方法可以直接调用 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { //如果异常,也同样调用HexDumpProxyFrontendHandler的closeOnFlush关闭输入管道 cause.printStackTrace(); HexDumpProxyFrontendHandler.closeOnFlush(ctx.channel()); } }
-
关键 1 保存传入的输入管道
-
关键 2 (核心) 当远端管道数据准备好,就可以转发到输入管道中
感谢各位的阅读,以上就是“proxy代理示例分析”的内容了,经过本文的学习后,相信大家对proxy代理示例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
原创文章,作者:carmelaweatherly,如若转载,请注明出处:https://blog.ytso.com/222901.html