bind方法会执行到doBind,关键在于initAndRegister
->init
和doBind0
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// 和上面的流程差不多,加了点异步处理
...
}
}
init
initAndRegister
主要是调用init
,这里添加了个ChannelInitializer,后者的回调行为是添加一个ServerBootstrapAcceptor,这定义了ServerChannel的行为
@Override
void init(Channel channel) throws Exception {
...
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
疑问:为什么不直接执行
pipeline.addLast
,而要间接通过ChannelInitializer
去添加呢?
然后看下ServerBootstrapAcceptor::channelRead,其行为是监听到读来的Channel时:
- 帮助注册用户设置的childHandler(以便触发回调注册)
- 并将其注册到childGroup上:
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler); // 触发设置的回调
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
try {
// 把Channel注册到childGroup
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
疑问:ServerBootstrapAcceptor何时会被触发channelRead?
doBind0
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
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());
}
}
});
}
// ChannelFutureListener.java
ChannelFutureListener CLOSE_ON_FAILURE = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
future.channel().close();
}
}
};
顾名思义,doBind0做真正的端口绑定。channel.bind
绑定到了端口,还加了个失败时关闭ServerChannel的监听器。
总结
ServerBootstrap::bind
- 通过为ServerChannel绑定ServerBootstrapAcceptor,定义了只要收到Channel就进行设置,并注册到childGroup。
- 然后通过doBind0监听端口,并绑定了监听器
CLOSE_ON_FAILURE
。