文章目录
四:接收网络连接
1. 接收客户端连接核心流程框架总览
- 在do…while{…}
循环read loop中不断的调用JDK NIO
serverSocketChannel.accept()方法来接收完成三次握手的客户端连接
NioSocketChannel。 - 将接收到的客户端连接NioSocketChannel临时保存在
List<Object> readBuf
集合中,后续会服务端NioServerSocketChannel的pipeline中通过ChannelRead事件来传递,最终会在ServerBootstrapAcceptor这个ChannelHandler中被处理初始化,并将其注册到Sub Reator Group中。
main reactor线程退出read loop循环的条件有两个:
- 在限定的16次读取中,已经没有新的客户端连接要接收了。退出循环。
- 从NioServerSocketChannel中读取客户端连接的次数达到了16次,无论此时是否还有客户端连接都需要退出循环。
private final class NioMessageUnsafe extends AbstractNioUnsafe {
//存放连接建立后,创建的客户端SocketChannel
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
//必须在Main Reactor线程中执行
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
//创建接收数据Buffer分配器(用于分配容量大小合适的byteBuffer用来容纳接收数据)
//在接收连接的场景中,这里的allocHandle只是用于控制read loop的循环读取创建连接的次数。以及判断是否该结束read loop转去执行异步任务。
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
//建立channel,并设置事件为read
//返回值localRead表示接收到了多少客户端连接,客户端连接通过accept方法只会一个一个的接收,所以这里的localRead正常情况下都会返回1
int localRead = doReadMessages(readBuf);
//已无新的连接可接收则退出read loop
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
//统计在当前事件循环中已经读取到的 Message数量(创建连接的个数)
allocHandle.incMessagesRead(localRead);
//判断是否已经读满16次,或者在16次读取以内已经没有新的客户端连接要接收了
} while (continueReading(allocHandle));
} catch (Throwable t) {
exception = t;
}
//遍历所有暂存在readBuf里面的客户端连接,传播通道可读事件
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
//当通道缓冲区可读,会调用fireChannelRead,触发通道可读事件
//初始化客户端SocketChannel,并将其绑定到Sub Reactor线程组中的一个Reactor上
pipeline.fireChannelRead(readBuf.get(i));
}
//清除本次accept 创建的客户端SocketChannel集合
readBuf.clear();
allocHandle.readComplete();
//通道缓冲区读完,调用fireChannelReadComplete,触发通道读完事件
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
-
首先断言,确保在Main Reactor线程中执行。而main reactor中主要注册的是服务端NioServerSocketChannel,主要负责处理
OP_ACCEPT事件
,所以当前main reactor线程是在NioServerSocketChannel中执行接收连接的工作。 -
通过config()获取ChannelConfig对象。该config是在初始化NioServerSocketChannel时设置的。
public NioServerSocketChannel(ServerSocketChannel channel) { //设置非阻塞 super(null, channel, SelectionKey.OP_ACCEPT); //相当于把本类和ServerSocketChannel存储配置中 config = new NioServerSocketChannelConfig(this, javaChannel().socket()); }
接着获取pipeline()。
RecvByteBufAllocator.Handle allocHandle
统计read loop中接收客户端连接的次数,以及判断是否该结束read loop转去执行异步任务。当达到16次之后会退出循环。
当这一切准备就绪之后,main reactor线程就开始在do{....}while(...)
循环中接收客户端连接了。
- 在 read loop中通过调用
doReadMessages函数
接收完成三次握手的客户端连接,底层会调用到JDK NIO ServerSocketChannel的accept方法,从内核全连接队列中取出客户端连接。返回值localRead表示接收到了多少客户端连接,客户端连接通过accept方法只会一个一个的接收,所以这里的localRead正常情况下都会返回1
protected int doReadMessages(List<Object> buf) throws Exception {
//建立连接
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
//如果此时并没有客户端连接时accept调用就会立刻返回null并不会阻塞。因为设置的非阻塞。
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
- 随后将接收到的客户端连接占时存放到
List<Object> readBuf
集合中。
private final List<Object> readBuf = new ArrayList<Object>();
- allocHandle.incMessagesRead(localRead)统计本次事件循环中接收到的客户端连接个数,最后在read loop末尾通过
allocHandle.continueReading
判断是否达到了限定的16次。从而决定main reactor线程是继续接收客户端连接还是转去执行异步任务。
满足退出条件后,随后开始遍历readBuf,在NioServerSocketChannel的pipeline中传播ChannelRead事件。
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
//当通道缓冲区可读,会调用fireChannelRead,触发通道可读事件
//初始化客户端SocketChannel,并将其绑定到Sub Reactor线程组中的一个Reactor上
pipeline.fireChannelRead(readBuf.get(i));
}
最终pipeline中的ChannelHandler(ServerBootstrapAcceptor)会响应ChannelRead事件,并在相应回调函数中初始化客户端NioSocketChannel,并将其注册到Sub Reactor Group中。此后客户端NioSocketChannel绑定到的sub reactor就开始监听处理客户端连接上的读写事件了。
Netty整个接收客户端的逻辑过程如下图步骤1,2,3所示。
2. RecvByteBufAllocator简介
在创建NioServerSocketChannel的时候会创建NioServerSocketChannelConfig
public NioServerSocketChannel(ServerSocketChannel channel) {
//设置非阻塞
super(null, channel, SelectionKey.OP_ACCEPT);
//相当于把本类和ServerSocketChannel存储配置中
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
在创建NioServerSocketChannelConfig的时候会创建RecvByteBufAllocator
public DefaultChannelConfig(Channel channel) {
this(channel, new AdaptiveRecvByteBufAllocator());
}
NioServerSocketChannel中的RecvByteBufAllocator实际类型为AdaptiveRecvByteBufAllocator
,顾名思义,这个类型的RecvByteBufAllocator可以根据Channel上每次到来的IO数据大小来自适应动态调整ByteBuffer的容量。
对于服务端NioServerSocketChannel来说,它上边的IO数据就是客户端的连接,它的长度和类型都是固定的,所以在接收客户端连接的时候并不需要这样的一个ByteBuffer来接收,我们会将接收到的客户端连接存放在List<Object> readBuf
集合中
对于客户端NioSocketChannel来说,它上边的IO数据时客户端发送来的网络数据,长度是不定的,所以才会需要这样一个可以根据每次IO数据的大小来自适应动态调整容量的ByteBuffer来接收。
2.1 RecvByteBufAllocator.Handle的获取
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
@Override
public RecvByteBufAllocator.Handle recvBufAllocHandle() {
if (recvHandle == null) {
recvHandle = config().getRecvByteBufAllocator().newHandle();
}
return recvHandle;
}
获取AdaptiveRecvByteBufAllocator执行newHandle创建Handle
public class AdaptiveRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator {
private final int minIndex;
private final int maxIndex;
private final int initial;
@Override
public Handle newHandle() {
//创建自适应动态调整容量的ByteBuffer分配器。
return new HandleImpl(minIndex, maxIndex, initial);
}
private final class HandleImpl extends MaxMessageHandle {
//最小容量在扩缩容索引表中的index
private final int minIndex;
//最大容量在扩缩容索引表中的index
private final int maxIndex;
//当前容量在扩缩容索引表中的index 初始33 对应容量2048
private int index;
//预计下一次分配buffer的容量,初始:2048
private int nextReceiveBufferSize;
//是否缩容
private boolean decreaseNow;
HandleImpl(int minIndex, int maxIndex, int initial) {
this.minIndex = minIndex;
this.maxIndex = maxIndex;
//在扩缩容索引表中二分查找到最小大于等于initial 的容量
index = getSizeTableIndex(initial);
nextReceiveBufferSize = SIZE_TABLE[index];//2048
}
}
}
这个Handle
里边保存了每次从Channel
中读取IO数据
的容量指标,方便下次读取时分配合适大小的buffer
。
每次在使用allocHandle
前需要调用allocHandle.reset(config);
重置里边的统计指标。
public abstract class MaxMessageHandle implements ExtendedHandle {
private ChannelConfig config;
//每次事件轮询时,最多读取16次
//可在启动配置类ServerBootstrap中通过ChannelOption.MAX_MESSAGES_PER_READ选项设置。
private int maxMessagePerRead;
//本次事件轮询总共读取的message数,NioServerSocketChannel指的是接收连接的数量
//用于统计read loop中总共接收的连接个数,NioSocketChannel中表示读取数据的次数
//每次read loop循环后会调用allocHandle.incMessagesRead增加记录接收到的连接个数
private int totalMessages;
//本次事件轮询总共读取的字节数,主要用于sub reactor在接收客户端NioSocketChannel上的网络数据用的
private int totalBytesRead;
public void reset(ChannelConfig config) {
this.config = config;
//默认每次最多读取16次
maxMessagePerRead = maxMessagesPerRead();
totalMessages = totalBytesRead = 0;
}
}
- maxMessagePerRead:用于控制每次read loop里最大可以循环读取的次数,默认为16次,可在启动配置类
ServerBootstrap
中通过ChannelOption.MAX_MESSAGES_PER_READ
选项设置。 - totalMessages:用于统计read loop中总共接收的连接个数,每次read loop循环后会调用
allocHandle.incMessagesRead
增加记录接收到的连接个数。
@Override
public final void incMessagesRead(int amt) {
//增加记录接收到的连接个数。
totalMessages += amt;
}
- totalBytesRead:用于统计在read loop中总共接收到客户端连接上的数据大小,这个字段主要用于sub reactor在接收客户端NioSocketChannel上的网络数据用的,本文我们介绍的是main reactor接收客户端连接,所以这里并不会用到这个字段。这个字段会在sub reactor每次读取完NioSocketChannel上的网络数据时增加记录。
public void lastBytesRead(int bytes) {
//增加上次读取接收到的客户端数据大小,记录总读取个数
lastBytesRead = bytes;
if (bytes > 0) {
totalBytesRead += bytes;
}
}
- 每次read loop末尾会调用
allocHandle.continueReading()
方法来判断读取连接次数是否已满16次,来决定main reactor线程是否退出循环。
do {
//建立channel,并设置事件为read
//返回值localRead表示接收到了多少客户端连接,客户端连接通过accept方法只会一个一个的接收,所以这里的localRead正常情况下都会返回1
int localRead = doReadMessages(readBuf);
//已无新的连接可接收则退出read loop
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
//统计在当前事件循环中已经读取到的 Message数量(创建连接的个数)
allocHandle.incMessagesRead(localRead);
//判断是否已经读满16次,或者在16次读取以内已经没有新的客户端连接要接收了
} while (continueReading(allocHandle));
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
return config.isAutoRead() &&
//respectMaybeMoreData=false:表示是否满载而归都继续读取,直到读取不到数据在退出循环。
//maybeMoreDataSupplier.get():判断本次读取byteBuffer是否满载而归。respectMaybeMoreData=true表示如果本次循环装满了byteBuffer,则后面可能还有数据,在次读取。如果byteBuffer没装满则退出循环。
(!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
//读取次数是否超过16次,超过则退出循环
totalMessages < maxMessagePerRead &&
//当客户端NioSocketChannel上的OP_READ事件活跃时,sub reactor线程在read loop中是否读取到了网络数据。没读到则退出read-loop
totalBytesRead > 0;
//这里有一个bug
// totalBytesRead字段主要记录sub reactor线程在处理客户端NioSocketChannel中OP_READ事件活跃时,总共在read loop中读取到的网络数据,
// 而这里是main reactor线程在接收客户端连接所以这个字段并不会被设置。totalBytesRead字段的值在本文中永远会是0。
//所以每次main会接收到一个accept事件就返回。
//新版本添加了ignoreBytesRead 参数,用于忽略mian Reactor
}
totalMessages < maxMessagePerRead
:判断main reactor线程在read loop中的读取次数是否超过了16次。如果超过16次就会返回false,main reactor线程退出循环。totalBytesRead > 0
:当客户端NioSocketChannel上的OP_READ事件活跃时,sub reactor线程在read loop中是否读取到了网络数据。没读到则退出read-loop。
3. doReadMessages接收客户端连接
protected int doReadMessages(List<Object> buf) throws Exception {
//建立连接
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
//如果此时并没有客户端连接时accept调用就会立刻返回null并不会阻塞。因为设置的非阻塞。
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
- 获取jdk原生ServerSocketChannel
@Override
protected ServerSocketChannel javaChannel() {
return (ServerSocketChannel) super.javaChannel();
}
- 通过
JDK NIO 原生
的ServerSocketChannel
的accept方法
获取JDK NIO 原生
客户端连接SocketChannel
。
public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
@Override
public SocketChannel run() throws IOException {
return serverSocketChannel.accept();
}
});
} catch (PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}
3.1 创建客户端NioSocketChannel
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
}
这里会注册OP_READ事件
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
3.2 对比NioSocketChannel与NioServerSocketChannel的不同
-
Channel的层次不同
- 由于客户端NioSocketChannel是在main reactor接收连接时在服务端NioServerSocketChannel中被创建的,所以在创建客户端NioSocketChannel的时候会通过构造函数指定了parent属性为
NioServerSocketChanel
。并将JDK NIO 原生
的SocketChannel
封装进Netty的客户端NioSocketChannel
中。
而在Reactor启动过程中创建
NioServerSocketChannel
的时候parent属性
指定是null
。因为它就是顶层的Channel
,负责创建客户端NioSocketChannel
。 - 由于客户端NioSocketChannel是在main reactor接收连接时在服务端NioServerSocketChannel中被创建的,所以在创建客户端NioSocketChannel的时候会通过构造函数指定了parent属性为
-
向Reactor注册的IO事件不同
- 客户端NioSocketChannel向Sub Reactor注册的是
SelectionKey.OP_READ事件
,而服务端NioServerSocketChannel向Main Reactor注册的是SelectionKey.OP_ACCEPT事件
。
- 客户端NioSocketChannel向Sub Reactor注册的是
-
功能属性不同造成继承结构的不同
-
客户端NioSocketChannel
继承的是AbstractNioByteChannel
,而服务端NioServerSocketChannel
继承的是AbstractNioMessageChannel
。
客户端
NioSocketChannel
主要处理的是服务端与客户端的通信,这里涉及到接收客户端发送来的数据,而Sub Reactor线程
从NioSocketChannel
中读取的正是网络通信数据单位为Byte
。
服务端
NioServerSocketChannel
主要负责处理OP_ACCEPT事件
,创建用于通信的客户端NioSocketChannel
。这时候客户端与服务端还没开始通信,所以Main Reactor线程
从NioServerSocketChannel
的读取对象为Message
。这里的Message
指的就是底层的SocketChannel
客户端连接。
客户端NioSocketChannel
结构如下:
4. ChannelRead事件的响应
main reactor就会退出read loop循环后,readBuf会存所有accept的客户端channel。随后遍历该集合,在NioServerSocketChannel的pipeline中传播ChannelRead事件。
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
//当通道缓冲区可读,会调用fireChannelRead,触发通道可读事件
//初始化客户端SocketChannel,并将其绑定到Sub Reactor线程组中的一个Reactor上
pipeline.fireChannelRead(readBuf.get(i));
}
//清除本次accept 创建的客户端SocketChannel集合
readBuf.clear();
最终ChannelRead事件
会传播到ServerBootstrapAcceptor
中,这里正是Netty处理客户端连接的核心逻辑所在。
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
//将childHandler添加到子channel上
child.pipeline().addLast(childHandler);
//将父childOptions设置子channel上
setChannelOptions(child, childOptions, logger);
//将父childAttrs设置子channel上
setAttributes(child, childAttrs);
try {
/**
* 1:在Sub Reactor线程组中选择一个Reactor绑定
* 2:将客户端SocketChannel注册到绑定的Reactor上
* 3:SocketChannel注册到sub reactor中的selector上,并监听OP_READ事件
* */
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);
}
}
}
-
这里会将设置的child前缀的配置初始化到NioSocketChannel中。
经过ServerBootstrapAccptor#chanelRead回调
的处理之后,此时客户端NioSocketChannel中pipeline的结构为:
随后会将初始化好的客户端NioSocketChannel向Sub Reactor Group中注册,并监听OP_READ事件
。
如下图中的步骤3所示:
5. 向SubReactorGroup中注册NioSocketChannel
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
- 从Sub Reactor Group中选取一个Sub Reactor进行绑定
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
@Override
public ChannelFuture register(Channel channel) {
//简单的遍历EventLoop,然后将channel注册到eventLoop上
return next().register(channel);
}
}
- 向绑定的Sub Reactor上注册NioSocketChannel
@Override
public ChannelFuture register(Channel channel) {
//注册channel到绑定的Reactor上
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
//将channel注册selector上,事件默认0,将accept和父handler注册到pipe,以及剔除初始化的handler
promise.channel().unsafe().register(this, promise);
return promise;
}
这里的promise.channel()
为NioSocketChannel
。底层的unsafe操作类为NioByteUnsafe
。
当注册任务提交后,此时绑定的Sub Reactor线程
启动。
5.1 register0
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
//1.查看注册操作是否已经取消,或者通道是否仍然打开,因为它可能在注册调用在 eventLoop 之外的同时关闭
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
//如果频道从未注册过,则为 true,否则为 false
boolean firstRegistration = neverRegistered;
//2.channel注册选择器上
doRegister();
//标记注册过
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
//3.初始化channelPipeline,回调pipeline中添加的ChannelInitializer的handlerAdded方法
//主要将accept和父handler注册到pipe,以及剔除初始化的handler
pipeline.invokeHandlerAddedIfNeeded();
//4.设置regFuture为success,触发operationComplete回调,将bind操作放入Reactor的任务队列中,等待Reactor线程执行。
//会触发io.netty.channel.ChannelFutureListener#operationComplete,进行端口绑定
safeSetSuccess(promise);
//5.通道注册完成后,成功绑定NioEventLoop线程后,会调用fireChannelRegistered,触发通道注册事件
pipeline.fireChannelRegistered();
// Only fire a channelActive if the channel has never been registered. This prevents firing
// multiple channel actives if the channel is deregistered and re-registered.
//对于服务端ServerSocketChannel来说 只有绑定端口地址成功后 channel的状态才是active的。
//此时绑定操作作为异步任务在Reactor的任务队列中,绑定操作还没开始,所以这里的isActive()是false
if (isActive()) {
//6.当通道激活完成后(所有的业务处理器添加,注册的异步任务完成,并且NioEventLoop线程绑定的异步任务完成),会调用fireChannelActive,触发通道激活事件
if (firstRegistration) {
//触发channelActive事件
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
- 这里
doRegister()方法
将NioSocketChannel注册到Sub Reactor中的Selector
上。 - 随后调用
pipeline.invokeHandlerAddedIfNeeded()
回调客户端NioSocketChannel上pipeline中的所有ChannelHandler的handlerAdded方法
,此时pipeline
的结构中只有一个ChannelInitializer
。最终会在ChannelInitializer#handlerAdded
回调方法中初始化客户端NioSocketChannel
的pipeline
。 - 此时客户端NioSocketChannel中的pipeline中的结构就变为了我们自定义的样子,在示例代码中我们自定义的
ChannelHandler
为EchoServerHandler
。
- 当客户端NioSocketChannel中的pipeline初始化完毕后,netty就开始调用
safeSetSuccess(promise)方法
回调regFuture
中注册的ChannelFutureListener
,通知客户端NioSocketChannel已经成功注册到Sub Reactor上了。
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
在服务端NioServerSocketChannel注册的时候我们会在listener中向Main Reactor提交bind绑定端口地址任务
。但是在NioSocketChannel
注册的时候,只会在listener
中处理一下注册失败的情况。
-
当Sub Reactor线程通知ChannelFutureListener注册成功之后,随后就会调用
pipeline.fireChannelRegistered()
在客户端NioSocketChannel的pipeline中传播ChannelRegistered事件
。 -
紧接着判断是否处于激活状态,服务端NioServerSocketChannel判断是否激活的标准为端口是否绑定成功。
-
@Override public boolean isActive() { // As java.nio.ServerSocketChannel.isBound() will continue to return true even after the channel was closed // we will also need to check if it is open. //查看是否打开连接,或者是否绑定 return isOpen() && javaChannel().socket().isBound(); }
-
客户端
NioSocketChannel
判断是否激活的标准为是否处于Connected状态
。那么显然这里肯定是处于connected状态
的。 -
public boolean isActive() { //判断是否处于连接状态 SocketChannel ch = javaChannel(); return ch.isOpen() && ch.isConnected(); }
-
NioSocketChannel
已经处于connected状态
,这里并不需要绑定端口,所以这里的isActive()
返回true
。
- 调用
pipeline.fireChannelActive()
在NioSocketChannel中的pipeline传播ChannelActive事件
,最终在pipeline
的头结点HeadContext
中响应并注册OP_READ事件
到Sub Reactor
中的Selector
上。
if (isActive()) {
//6.当通道激活完成后(所有的业务处理器添加,注册的异步任务完成,并且NioEventLoop线程绑定的异步任务完成),会调用fireChannelActive,触发通道激活事件
if (firstRegistration) {
//触发channelActive事件
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
public abstract class AbstractNioChannel extends AbstractChannel { {
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
//查看SelectionKey是否有效
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
/**
* 1:ServerSocketChannel 初始化时 readInterestOp设置的是OP_ACCEPT事件
* 2:SocketChannel 初始化时 readInterestOp设置的是OP_READ事件
* */
if ((interestOps & readInterestOp) == 0) {
//注册accept事件
selectionKey.interestOps(interestOps | readInterestOp);
}
}
}
此时Netty中主从Reactor组的结构就变为: