千家信息网

proxy代理示例分析

发表于:2025-12-03 作者:千家信息网编辑
千家信息网最后更新 2025年12月03日,这篇文章主要讲解了"proxy代理示例分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"proxy代理示例分析"吧!准备工作:将代码全部整理clone
千家信息网最后更新 2025年12月03日proxy代理示例分析

这篇文章主要讲解了"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 {//继承  ChannelInitializer    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中的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代理示例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0