端口绑定,大部分是在AbstractChannel的内部类AbstractUnsafe的bind()这个方法里面完成的。主要完成了两件事情:
1、调用jdk底层绑定端口
2、传播channelActive事件。
在AbstractBoostrap的
doBind()这个方法的initAndRegister()调用后面,有一个doBind0(),一层一层进去,到达AbstractChannelHeadContext的这个方法:
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
}
点击HeadContext的实现,再进去一层就能看到AbstractUnsafe的bind的实现:
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.isRoot()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
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);
}
看NioServerSocketChannel的doBInd的实现:
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
就是调用jdk底层实现端口绑定了。
观察到这一段:
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
wasActive实在doBind()之前赋值,这段代码的意思是:如果doBInd之前isActive为false,之后isActive()为true,那么就传播事件。也就是,之前没有传播过,后面绑定端口完成之后,就开始传播。
一样的道理,我们层层进入fireChannelActive最后到达HeadContext的channelAcitve方法:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
readIfIsAutoRead();
}
这里又做了两件事:
1、传播事件
2、把在用户代码注册的读事件注册上去。
在传播事件这件事情上,我们一层一层点进去,在AbstractChannelContextHandler的这个方法里面:
private void invokeChannelActive() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelActive(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelActive();
}
}
channelActive这个方法,找我们用户代码自己实现的channelActive,就能看到这个:
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("channelActive");
}
是不是很神奇!!
在把在用户代码注册的读事件注册上去这里,点击进去readIfIsAutoRead()这个方法,还是实现HeadContext的read方法,AbstractNioChannel的doBegainRead()方法:
@Override
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);
}
}
可以很容易知道,readInterestOps就把读事件传递进去了。