转载自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
版权声明:本文为博主原创文章,转载请附上博文链接!

最新文章

  1. 深入理解javascript选择器API系列第三篇——h5新增的3种selector方法
  2. input输入框限制仅能输入数字且规定数字长度(使用与输入手机号)
  3. ギリギリ eye (优先队列)
  4. 代码优化—From <effective C++>
  5. eclipse 启动tomcat报Spring错误 Error creating bean with name 'serviceOrderBiz': Injection of autowired dependencies failed
  6. seajs 和spm的使用简介
  7. Intra Luma Prediction
  8. Java学习----对象的状态和行为
  9. 将使用netTcp绑定的WCF服务寄宿到IIS7上全记录 (这文章也不错)
  10. structs2标签
  11. Java基础之静态变量
  12. 一些常见warning的原因和解决方法
  13. 《半吊子全栈系列:Boostrap3》
  14. java复习(7)---集合类、泛型
  15. 从一道例题谈Arrays.toString()与其他String的转换方法
  16. react看这篇就够了(react+webpack+redux+reactRouter+sass)
  17. teeporxy.go
  18. SVG矢量图学习实例
  19. Python爬虫与数据图表的实现
  20. 使用matlab和ISE 创建并仿真ROM IP核

热门文章

  1. Vue源码解析,keep-alive是如何实现缓存的?
  2. jqgrid 自定义文本框、选择框等查询
  3. 转圈游戏C++
  4. C++ 2的幂次方表示
  5. 第7章 Spark SQL 的运行原理(了解)
  6. MariaDB二进制安装
  7. Solon详解(五)- Solon扩展机制之Solon Plugin
  8. 牛客网PAT练习场-有几个PAT
  9. Java动态代理(三)——Cglib动态代理
  10. Java多线程_阻塞队列