接上文,上文我们分析了initAndRegister的过程,现在接着分析tag2的doBind0过程:
|
|
channel.bind(localAddress, promise)内部执行了如下代码:
|
|
ChannelPipeline
在上文中我们还留有pipeline.fireChannelRegistered()没有分析。接下来就介绍ChannelPipeline这个重要的类。DefaultChannelPipeline是ChannelPipeline的默认实现类,内部维护了final AbstractChannelHandlerContext head和final AbstractChannelHandlerContext tail两个指针,分别指向链表的头尾。而AbstractChannelHandlerContext本身是一个双向链表结构,volatile AbstractChannelHandlerContext next和volatile AbstractChannelHandlerContext prev分别为每个节点的双向指针。DefaultChannelHandlerContext是AbstractChannelHandlerContext的默认实现,其中包含一个ChannelHandler属性。这就是一个典型的Intercepting Filter模式。好了,现在我们回头再看一次ServerBootstrap.init()方法。它对NioServerSocketChannel的ChannelPipeline执行了两次addLast()操作:
|
|
第一次添加的是ServerBootstrap的handler,第二次添加的是匿名类ChannelInitializer的实现。所以此时负责监听的NioServerSocketChannel的职责链如下:HEAD->AbstractServerBootstrap$handler->ServerBootstrap$1->TAIL。
现在我们再来看pipeline.fireChannelRegistered()方法,它依次执行了下面的方法:
|
|
我们知道handler链是HEAD->AbstractServerBootstrap$handler->ServerBootstrap$1->TAIL。findContextInbound()方法返回的next就是ServerBootstrap$handler,找到的第一个没有被注解为Skip的handler返回。这里Skip注解了的方法表示职责链中碰到该方法时跳过。在构造方法中会通过skipFlags()方法初始化skipFlags。
|
|
在EchoServer对应的职责链变成HEAD->LoggingHandler->ServerBootstrap$1->TAIL,所以找到的第一个handler是LoggingHandler,执行如下方法:
|
|
记录日志之后,该方法会继续调用ctx.fireChannelRegistered(),所以重复上面的过程,找到职责链中的下一个handler为匿名类ServerBootstrap$1。
由于匿名类继承于ChannelInitializer,所以执行ChannelInitializer.channelRegistered()方法:
|
|
方法中回调了ServerBootstrap$1中的initChannel()方法:
|
|
该方法中把ServerBootstrapAcceptor添加到pipeline中,所以此时职责链变成了HEAD->LoggingHandler->ServerBootstrap$1->ServerBootstrapAcceptor->TAIL
。接着pipeline.remove(this)
语句把自己从pipeline中删掉了,所以职责链变成HEAD->LoggingHandler->ServerBootstrapAcceptor->TAIL
。接着继续往下找,找到TAIL这个handler,并执行对应的channelRegistered方法。至此pipeline.fireChannelRegistered()
方法完成。
doBind
分析完pipeline,我们再来看pipeline.bind(localAddress, promise)
方法,它最终调用了tail.bind(localAddress, promise)
方法,注意是tail不是head,bind操作在职责链中是反向进行的。
|
|
根据上面的分析,EchoServer的职责链完成register之后是这样的:HEAD->LoggingHandler->ServerBootstrapAcceptor->TAIL
。从TAIL向前,执行每个handler的bind方法。实际最后只执行了head中的bind方法:
|
|
其中真正执行bind操作的在doBind方法中:
|
|
绑定端口后执行invokeLater方法:
|
|
该方法只是把task加入到EventLoopGroup的队列中,并不会马上执行,需要等到safeSetSuccess(promise)执行完后的下个循环才会执行。
|
|
safeSetSuccess把状态设为成功以后,执行notifyListeners()方法,由于此时listeners为null,所以直接返回。
|
|
设置成功状态后,开始执行上面invokeLater中的task,也就是pipeline.fireChannelActive()
:
|
|
由于channel.config().isAutoRead()
默认返回true,所以执行channel.read()。
|
|
同样的,pipeline从后往前执行每个handler的read()方法,最后执行到head.read()
|
|
在NioServerSocketChannel初始化时,我们指定了readInterestOp为SelectionKey.OP_ACCEPT,也就是16。最后执行selectionKey.interestOps(interestOps | readInterestOp)
,等同于selectionKey.interestOps(SelectionKey.OP_ACCEPT)
,也就是向selector注册了accept事件的监听。至此bind操作完成,接着来看下一步sync()。
|
|
由于setSafeSuccess中已经吧状态设置为成功,所以await()方法会直接返回。到此ChannelFuture f = b.bind(PORT).sync()
完成。最后一步f.channel().closeFuture().sync()
,而由于closeFuture这个属性的执行结果一直没有赋值,所以一直处于wait状态。至此,主线程处于wait状态,并通过子线程无限循环,来完成客户端请求。
小结
- 通过channel()方法设置不同类型的Socket。通过childHandler()设置SocketChannel的职责链。
- bind()不同于JavaSocket的bind,主要完成initAndRegister()和doBind0()过程。
- initAndRegister中主要可以分为三个步骤:createChannel(), init(channel), channel.unsafe().register()。
- doBind0主要完成javaChannel().register(eventLoop().selector, 0, this)功能,并触发channelActive()事件,设置selectionKey.interestOps(SelectionKey.OP_ACCEPT)。