上一篇中netty注册流程分析一,分析到了channel注册到nioEventLoop中了,接下来就是doBind0方法
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
通过channel.bind内部最终会掉用pipeline上TailContext的bind方法,这个看下这篇文章
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
ObjectUtil.checkNotNull(localAddress, "localAddress");
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null, false);
}
return promise;
}
从方法中可以看出调用bind方法,并不是tail,而是它的上一个出站处理器,也就是HeadContext,HeadContext的bind方法又是调用channel内部unsafe的bind方法。
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
//省略....
assertEventLoop();
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
这个方法又主要有三个步骤,绑定到服务器端口doBind,提交一个触发channelActive的任务到队列中,promise的result设置为成功,通知监听器。
触发channel的channelActive方法,内部会调用HeadContext的channelActive
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
HeadContext直接转发给下一个inboundHandler的channelActive,再调用readIfIsAutoRead
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
而看到channel.read方法,程序又会执行太Context的read方法,参考channel、handler调用关系,而TailContext最终又是调用channel中Unsafe的beginRead方法
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
直到这边,netty才将NioServerSocketChannel注册到Selector的监听事件注册为accept。当NioEventLoop执行完队列所有的任务之后,在事件循环开始的地方又进入Selector.select的阻塞,监听客户端的连接。
最后总结一下,netty直接调用channel的相关方法时,会先转给pipeline,再由pipeline根据入站事件还是出站事件,转给HeadContext和TailContext,而TailContext在处理出站事件时,最终又回到channel的unsafe对象去执行相应的IO方法。
至此,netty只是将channel注册到selector上,且监听accept事件。下篇再来讲述下NioEventLoop的事件循环,及如何将新的连接channel注册到worker group上去监听read事件。