本文来源:http://blog.csdn.net/zxhoo/article/details/17964353
感谢博主。本文仅供参考学习。
前面有一篇文章分析过Bootstrap类如何引导NioSocketChannel。上篇文章简单讨论了一下Channel接口的方法,知道有四个方法用来查询Channel的状态:isOpen()、isRegistered()、isActive()和isWritable()。这篇文章结合Bootstrap分析一下前三个方法,看看NioSocketChannel是如何到达这三个状态的。
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状态。