Netty 第十篇:Netty 如何处理新连接(OP_ACCEPT)
📅 2026-05-29 ✍️ 原创 🏷️ Java / NIO / Netty / 源码分析 / 连接处理
在上一篇文章中,我们详细分析了 NioEventLoop 的 run() 方法。其中在处理 IO 事件时,如果是 OP_ACCEPT 事件,会调用 unsafe.read() 方法。
这一篇,我们就顺着这条线索往下走,看看 Netty 是如何把一个操作系统层面的 TCP 连接,转换成 Netty 中的 NioSocketChannel,并最终注册到 Worker 线程池中的。
一、从 unsafe.read() 说起
当 NioEventLoop 检测到 NioServerSocketChannel 上有 OP_ACCEPT 事件时,会执行以下调用链:
NioEventLoop.processSelectedKey()
-> AbstractNioMessageChannel.NioMessageUnsafe.read()
这里要注意,服务端的 Unsafe 是 NioMessageUnsafe。它的核心逻辑是:读取消息(连接),并把这些消息(连接)传递给 Pipeline。
二、doReadMessages:构建 NioSocketChannel
NioMessageUnsafe.read() 会调用一个抽象方法 doReadMessages(readBuf)。
对于 NioServerSocketChannel 来说,实现如下:
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
// 1. 调用 JDK 原生方法,接受一个新连接
SocketChannel ch = SocketUtils.accept(javaChannel());
if (ch != null) {
// 2. 将 JDK 的 SocketChannel 包装成 Netty 的 NioSocketChannel
buf.add(new NioSocketChannel(this, ch));
return 1;
}
return 0;
}
三、触发 channelRead 事件
当 doReadMessages 执行完毕后,NioMessageUnsafe 会拿到一个装满了 NioSocketChannel 的 List。
接着,它会将这些 Channel 作为“消息”,通过 Pipeline 向下传播:
int size = readBuf.size();
for (int i = 0; i < size; i++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
注意:这里传播的不是 ByteBuf,而是一个 Channel 对象。
四、ServerBootstrapAcceptor:连接的分发器
还记得我们在前面章节讲过的吗?在 ServerBootstrap.init() 方法中,会给服务端的 Channel 添加一个特殊的 Handler —— ServerBootstrapAcceptor。
当 pipeline.fireChannelRead(nioSocketChannel) 传到这个 Handler 时,它的 channelRead 方法会被调用。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 1. 强制类型转换(msg 就是 NioSocketChannel)
final Channel child = (Channel) msg;
// 2. 为这个新连接配置 Pipeline(添加用户定义的 Handlers)
child.pipeline().addLast(childHandler);
// 3. 设置 Channel 的属性(Options 和 Attributes)
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
// 4. 注册到 ChildGroup(Worker 线程池)
childGroup.register(child).addListener(...);
}
Main Reactor(ParentGroup)负责 Accept,然后把 Channel 交给 Sub Reactor(ChildGroup)负责 Read/Write。
五、NioSocketChannel 的注册流程
childGroup.register(child) 的流程,我们在第七篇中已经详细讲过,这里做一个对比:
| 对比项 | NioServerSocketChannel | NioSocketChannel |
|---|---|---|
| 注册目标 | ParentGroup (Boss) | ChildGroup (Worker) |
| Unsafe 类型 | NioMessageUnsafe | NioByteUnsafe |
| doRegister | javaChannel().register(selector, 0, this) | javaChannel().register(selector, 0, this) |
| doBeginRead | interestOps(OP_ACCEPT) | interestOps(OP_READ) |
1. 注册逻辑复用
注册代码是完全复用的。都是调用 AbstractChannel.register() → register0() → doRegister()。
2. 关注事件不同
唯一的区别在于最后的 doBeginRead():
// NioServerSocketChannel
protected void doBeginRead() throws Exception {
selectionKey.interestOps(OP_ACCEPT);
}
// NioSocketChannel
protected void doBeginRead() throws Exception {
selectionKey.interestOps(OP_READ);
}
一旦注册完成并设置好 InterestOps,这个连接就正式进入了工作模式,开始监听读事件了。
六、总结
Netty 处理新连接的完整流程如下:
| 步骤 | 核心动作 |
|---|---|
| 1. 检测事件 | NioEventLoop 发现 OP_ACCEPT |
| 2. 接受连接 | NioMessageUnsafe.read() |
| 3. 包装 Channel | JDK SocketChannel → NioSocketChannel |
| 4. 传播事件 | Pipeline.fireChannelRead(NioSocketChannel) |
| 5. 分发连接 | ServerBootstrapAcceptor 接住连接 |
| 6. 注册 Worker | childGroup.register(NioSocketChannel) |
| 7. 监听读事件 | interestOps(OP_READ) |
— 原创文章,转载请注明出处 —

浙公网安备 33010602011771号