zoukankan      html  css  js  c++  java
  • nioServerChannel的的状态

    转载自https://blog.csdn.net/zxhoo/article/details/17964353

    Channel继承层次图
    分析上面提到的三个状态的时候,会去看Channel继承层次里某些类的代码,为了方便参考,我画了一张(不太严格的)UML类图,如下所示:

    open状态
    先从isOpen()方法入手,isOpen()方法是在AbstractNioChannel抽象类里实现的,下面是这个类的关键代码:

    public abstract class AbstractNioChannel extends AbstractChannel {
    ...
    private final SelectableChannel ch;
    ...
        @Override
        public boolean isOpen() {
            return ch.isOpen();
        }
    ...
    }
    可以看出来,Netty的Channel是否open取决于Java的SelectableChannel是否open。换句话说,只要找出Netty何时open了这个SelectableChannel,就可以知道Channel何时到达了open状态。从Bootstrap的connect()方法开始顺藤摸瓜就能找出答案:
    Bootstrap.connect(String inetHost, int inetPort)
    -> Bootstrap.doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress)
    -> AbstractBootstrap.initAndRegister()
    -> BootstrapChannelFactory.newChannel()
    -> NioSocketChannel()
    -> NioSocketChannel.newSocket()
    -> SocketChannel.open()
    重点看看initAndRegister()方法:
    // AbstractBootstrap.java
    final ChannelFuture initAndRegister() {
    final Channel channel = channelFactory().newChannel();
    try {
    init(channel);
    } catch (Throwable t) {
    channel.unsafe().closeForcibly();
    return channel.newFailedFuture(t);
    }

    ChannelPromise regPromise = channel.newPromise();
    group().register(channel, regPromise);
    ...
            return regPromise;
        }
    initAndRegister()方法先创建了Channel实例(此时Channel已经处于open状态),然后把它注册到group里,所以大概能够知道,Channel是在open之后进入registered状态的,如下图所示:


    registered状态
    为了证明上面的猜测,我们从NioEventLoopGroup.register()方法接着看代码。NioEventLoopGroup并没有实现register()方法,真正的实现是在它的超类MultithreadEventLoopGroup里:

    // MultithreadEventLoopGroup.java
    @Override
    public ChannelFuture register(Channel channel, ChannelPromise promise) {
    return next().register(channel, promise);
    }
    根据这篇文章的介绍,next()方法返回的是一个NioEventLoop,看代码后知道,register()方法是在NioEventLoop的超类,SingleThreadEventLoop里实现的:
    // SingleThreadEventLoop.java
    @Override
    public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
    ...
    channel.unsafe().register(this, promise);
    return promise;
    }
    好吧,继续看代码,知道调用的是AbstractChannel.AbstractUnsafe.register()方法,这个方法又调用了AbstractUnsafe.register0()方法,在register0()方法里,registered字段被设置为true。而AbstractChannel的isRegistered()方法正好是通过这个字段来判断是否是registered状态:
    // AbstractChannel.java
    @Override
    public boolean isRegistered() {
    return registered;
    }
    也就是说,上面的猜测是正确的,Channel先进入open状态,然后通过把自己注册到group进入registered状态。


    active状态
    还是先看看isActive()方法是如何实现的(在NioSocketChannel里):

    // NioSocketChannel.java
    @Override
    public boolean isActive() {
    SocketChannel ch = javaChannel();
    return ch.isOpen() && ch.isConnected();
    }
    也就是说,NioSocketChannel的active状态取决于SocketChannel的状态。根据前面的分析知道,NioSocketChannel构造函数执行之后,SocketChannel已经处于open状态了,那么接下来就看SocketChannel的connect()方法是何时被调用的。回到Bootstrap类的doConnect()方法:
    Bootstrap.connect(String inetHost, int inetPort)
    -> Bootstrap.doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress)
    -> AbstractBootstrap.initAndRegister()
    Bootstrap.doConnect0(...)
    -> Channel.connect(SocketAddress remoteAddress, ChannelPromise promise
    doConnect()方法在initAndRegister()之后又调用了doConnect0()方法,doConnect0()方法调用了Channel的connect()方法。在AbstractChannel里有connect()方法的实现:
    // AbstractChannel.java
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
    return pipeline.connect(remoteAddress, promise);
    }
    也就是说,connect实际上是被当做事件交给pipeline去处理的,而且是个outbound事件,看DefaultChannelPipeline:
    // DefaultChannelPipeline.java
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
    return tail.connect(remoteAddress, promise);
    }
    tail是DefaultChannelHandlerContext实例:
    // DefaultChannelHandlerContext.java
    @Override
    public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
    return connect(remoteAddress, null, promise);
    }

    @Override
    public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
    ...
    final DefaultChannelHandlerContext next = findContextOutbound();
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
    next.invokeConnect(remoteAddress, localAddress, promise);
    } else {
    safeExecute(executor, new Runnable() {
    @Override
    public void run() {
    next.invokeConnect(remoteAddress, localAddress, promise);
    }
    }, promise, null);
    }

    return promise;
    }

        private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
            try {
                ((ChannelOutboundHandler) handler).connect(this, remoteAddress, localAddress, promise);
            } catch (Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        }
    三个参数版的connect()方法看起来很复杂,但无非就是做了两件事:先沿着pipeline往前找到第一个outbound类型的context,接着调用这个context的invokeConnect()方法。然后context又调用了handler的connect()方法,而pipeline里必定会有一个outbound类型的context/handler,这个context就是head,相应的handler是内部类HeadHandler:
    // DefaultChannelPipeline.java
    static final class HeadHandler implements ChannelOutboundHandler {
    protected final Unsafe unsafe;

        protected HeadHandler(Unsafe unsafe) {
            this.unsafe = unsafe;
        }
    ...
    @Override
        public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
            unsafe.connect(remoteAddress, localAddress, promise);
        }
    ...
    }
    HeadHandler只是调用了unsafe的connect()方法,unsafe是在构造函数里传进来的:
    public DefaultChannelPipeline(AbstractChannel channel) {
    ...
    HeadHandler headHandler = new HeadHandler(channel.unsafe());
    head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
    ...
    }
    Unsafe.connect()方法在AbstractNioChannel.AbstractNioUnsafe里实现,这个实现调用了AbstractNioChannel.doConnect()方法。doConnect()方法最终在NioSocketChannel里得以实现:
    // NioSocketChannel.java
    @Override
    protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
    if (localAddress != null) {
    javaChannel().socket().bind(localAddress);
    }

    boolean success = false;
    try {
    boolean connected = javaChannel().connect(remoteAddress);
    if (!connected) {
    selectionKey().interestOps(SelectionKey.OP_CONNECT);
    }
    success = true;
    return connected;
    } finally {
    if (!success) {
    doClose();
    }
    }
    }
    结论
    代码分析的很复杂,但结论很简单:被Bootstrap引导的NioSocketChannel在构造好之后就进入了open状态,之后通过把自己注册进EventLoop进入registered状态,接着连接服务器进入active状态。


    ---------------------
    作者:zxh0
    来源:CSDN
    原文:https://blog.csdn.net/zxhoo/article/details/17964353
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    如何随机生成指定数目的国标汉字?
    动态裁剪窗体标题栏
    N皇后问题
    谈谈五皇后问题
    一道趣味数学题
    In version 2.0, DB Query Analyzer will never be mistaken as virus by antivirus software
    从2.0版本开始,“万能数据库查询分析器”的中英文版本不再被误报成病毒
    Android_获取屏幕大小的两种方法
    广东电信公话201亲情月卡用户重复购买率模型的研究
    基于关系数据库的数据仓库星形模式下维使用原则的研究与探索
  • 原文地址:https://www.cnblogs.com/heroinss/p/9968988.html
Copyright © 2011-2022 走看看