proxy代理示例分析

这篇文章主要讲解了“proxy代理示例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“proxy代理示例分析”吧!

准备工作:

将代码全部整理clone到本地,IDE可以正常打开即可。 后边的分析也都一样。

proxy代理示例分析

核心分析

proxy的位置

proxy代理示例分析

框架图

此示例主要是进行的代理访问的数据转发。

proxy代理示例分析

  • 请求-》LocalServer=》RemoteServer

  • RemoteServer 返回数据=>LocalServer=>客户接收

代码分析
HexDumpProxy 主逻辑

代码预览:

proxy代理示例分析

我把原来代码中端口和域名修改了下,不影响观看

代码解读

        //准备好两个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 日志打印

不是必要的,是为了调试看方便使用 如图 proxy代理示例分析

接着看 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的管道里面,这是将客户过来的数据写入到远程管道里面

proxy代理示例分析

接着看 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代理示例分析”的内容了,经过本文的学习后,相信大家对proxy代理示例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

原创文章,作者:carmelaweatherly,如若转载,请注明出处:https://blog.ytso.com/222901.html

(0)
上一篇 2022年1月6日
下一篇 2022年1月6日

相关推荐

发表回复

登录后才能评论