zoukankan      html  css  js  c++  java
  • Netty(六)揭开 BootStrap 的神秘面纱

    6.1 客户端 BootStrap

    6.1.1 Channel 简介
    在 Netty 中,Channel 是一个 Socket 的抽象,它为用户提供了关于 Socket 状态(是否是连接还是断开)以及对 Socket
    的读写等操作。每当 Netty 建立了一个连接后, 都创建一个对应的 Channel 实例。
    除了 TCP 协议以外,Netty 还支持很多其他的连接协议, 并且每种协议还有 NIO(非阻塞 IO)和 OIO(Old-IO, 即传统的
    阻塞 IO)版本的区别。不同协议不同的阻塞类型的连接都有不同的 Channel 类型与之对应下面是一些常用的 Channel
    类型:
    6.1.2 NioSocketChannel 的创建
    Bootstrap 是 Netty 提供的一个便利的工厂类, 我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化。
    下面我先来看一个例子, 从客户端和服务器端分别分析一下 Netty 的程序是如何启动的。首先,让我们从客户端的代码
    片段开始: 
    EventLoopGroup group = new NioEventLoopGroup();
                try {
                    Bootstrap b = new Bootstrap();
                    b.group(group)
                            .channel(NioSocketChannel.class)
                            .option(ChannelOption.TCP_NODELAY, true)
                            .handler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                public void initChannel(SocketChannel ch) throws Exception {
                                    ChannelPipeline pipeline = ch.pipeline();
                                    //自定义协议解码器
                                    /** 入参有5个,分别解释如下
                                     maxFrameLength:框架的最大长度。如果帧的长度大于此值,则将抛出TooLongFrameException。
                                     lengthFieldOffset:长度字段的偏移量:即对应的长度字段在整个消息数据中得位置
                                     lengthFieldLength:长度字段的长度:如:长度字段是int型表示,那么这个值就是4(long型就是8)
                                     lengthAdjustment:要添加到长度字段值的补偿值
                                     initialBytesToStrip:从解码帧中去除的第一个字节数
                                     */
                                    pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                                    //自定义协议编码器
                                    pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                                    //对象参数类型编码器
                                    pipeline.addLast("encoder", new ObjectEncoder());
                                    //对象参数类型解码器
                                    pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                                    pipeline.addLast("handler",consumerHandler);
                                }
                            });
    
                    ChannelFuture future = b.connect("localhost", 8080).sync();
                    future.channel().writeAndFlush(msg).sync();
                    future.channel().closeFuture().sync();
                } catch(Exception e){
                    e.printStackTrace();
                }finally {
                    group.shutdownGracefully();
                }
    从上面的客户端代码虽然简单, 但是却展示了 Netty 客户端初始化时所需的所有内容:
    1、EventLoopGroup:不论是服务器端还是客户端, 都必须指定 EventLoopGroup。在这个例子中, 指定了
    NioEventLoopGroup, 表示一个 NIO 的 EventLoopGroup。
    2、ChannelType: 指定 Channel 的类型。 因为是客户端,因此使用了 NioSocketChannel。
    3、Handler: 设置处理数据的 Handler。
    下面我们继续深入代码,看一下客户端通过 Bootstrap 启动后,都做了哪些工作?我们看一下 NioSocketChannel 的
    类层次结构如下:
     
    回到我们在客户端连接代码的初始化 Bootstrap 中调用了一个 channel()方法,传入的参数是 NioSocketChannel.class,
    在这个方法中其实就是初始化了一个 ReflectiveChannelFactory 的对象:
     
    public B channel(Class<? extends C> channelClass) { 
    if (channelClass == null) { 
        throw new NullPointerException("channelClass"); 
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass)); 
    }
    而 ReflectiveChannelFactory 实现了 ChannelFactory 接口, 它提供了唯一的方法, 即 newChannel()方法,
    ChannelFactory, 顾名思义, 就是创建 Channel 的工厂类。进入到 ReflectiveChannelFactory 的 newChannel()方法中,
    我们看到其实现代码如下: 
    public T newChannel() {
     // 删除了 try...catch 块 
    return clazz.newInstance(); 
    }
    根据上面代码的提示,我们就可以得出:
    1、Bootstrap 中的 ChannelFactory 实现类是 ReflectiveChannelFactory。
    2、通过 channel()方法创建的 Channel 具体类型是 NioSocketChannel。
    Channel 的实例化过程其实就是调用 ChannelFactory 的 newChannel()方法,而实例化的 Channel 具体类型又是和初
    始化 Bootstrap 时传入的 channel()方法的参数相关。因此对于客户端的 Bootstrap 而言,创建的 Channel 实例就是
    NioSocketChannel。 
     
    6.1.3 客户端 Channel 的初始化
    前面我们已经知道了如何设置一个 Channel 的类型,并且了解到 Channel 是通过 ChannelFactory 的 newChannel()方
    法来实例化的, 那么 ChannelFactory 的 newChannel()方法在哪里调用呢?继续跟踪, 我们发现其调用链如下:
    在 AbstractBootstrap 的 initAndRegister()中调用了 ChannelFactory()的 newChannel()来创建一个 NioSocketChannel
    的实例,其源码如下:
    final ChannelFuture initAndRegister() {
            Channel channel = null;
    
            try {
                channel = this.channelFactory.newChannel();
                this.init(channel);
            } catch (Throwable var3) {
                if (channel != null) {
                    channel.unsafe().closeForcibly();
                    return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
                }
    
                return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
            }
    
            ChannelFuture regFuture = this.config().group().register(channel);
            if (regFuture.cause() != null) {
                if (channel.isRegistered()) {
                    channel.close();
                } else {
                    channel.unsafe().closeForcibly();
                }
            }
    
            return regFuture;
        }
    在 newChannel()方法中,利用反射机制调用类对象的 newInstance()方法来创建一个新的 Channel 实例,相当于调用
    NioSocketChannel 的默认构造器。NioSocketChannel 的默认构造器代码如下: 
  • 相关阅读:
    AWS上的游戏服务:Lumberyard + Amazon GameLift + Twitch
    Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
    iOS多线程与网络开发之小文件上传
    VMware-Fusion-7.0.0-2103067 Pro SN:序列号+ 百度云下载地址
    PCA的数学原理Matlab演示
    typedef,结构体,共用体,联合体
    XMPP系列(三)---获取好友列表、加入好友
    王立平--eclipse中改动android项目的版本
    linux入门
    机器学习(3)——多变量线性回归
  • 原文地址:https://www.cnblogs.com/flgb/p/13150073.html
Copyright © 2011-2022 走看看