原文链接:https://wangwei.one/posts/netty-new-connection-and-niosocketchannel-analyse.html
前面的一些章节,我们分析了Netty的三大组件 —— Channel 、EventLoop、Pipeline ,对Netty的工作原理有了深入的了解。在此基础上,我们来分析一下当Netty服务端启动后,Netty是如何处理新连接接入的。
本文内容主要分为以下四部分:
- 新连接检测
- NioSocketChannel创建
- NioSocketChannel初始化与注册
- NioSocketChannel注册READ兴趣集
新连接检测
前面,我们在讲 EventLoop的启动过程源码分析 时,解读过下面这段代码:
public final class NioEventLoop extends SingleThreadEventLoop {
...
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
...
try {
...
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 读取read事件
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
...
}
...
}
我们还是以服务端 NioServerSocketChannel 为例,它绑定的unsafe实例为 NioMessageUnsafe 。上面的 unsafe.read()
接口,会向下调用到 NioMessageUnsafe.read() 接口,如下:
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
...
private final class NioMessageUnsafe extends AbstractNioUnsafe {
// 用于保存新建立的 NioSocketChannel 的集合
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
// 确保在当前线程与EventLoop中的一致
assert eventLoop().inEventLoop();
// 获取 NioServerSocketChannel config配置
final ChannelConfig config = config();
// 获取 NioServerSocketChannel 绑定的 pipeline
final ChannelPipeline pipeline = pipeline();
// 获取RecvByteBuf 分配器 Handle
// 当channel在接收数据时,allocHandle 会用于分配ByteBuf来保存数据
// 关于allocHandle后面再去做详细介绍
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
// 重置已累积的所有计数器,并为下一个读取循环读取多少消息/字节数据提供建议
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
// 调用后面的 doReadMessages 接口,读取到message则返回1
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
// 对当前read循环所读取到的message数量计数+1
allocHandle.incMessagesRead(localRead);
// 判断是否继续读取message
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
// 调用pipeline传播ChannelRead事件
pipeline.fireChannelRead(readBuf.get(i));
}
// 清空readBuf
readBuf.clear();
allocHandle.readComplete();
// 调用pipeline传播 ChannelReadComplete 事件
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();
}
}
}
}
...
}
对于 doReadMessages(...)
的分析:
public class NioServerSocketChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.ServerSocketChannel {
...
// 读取消息
@Override
protected int doReadMessages(List<Object> buf)