Netty 客户端连接流程,以及底层三次握手是在哪个环节完成的
Netty 客户端的连接流程是一个非常经典的异步非阻塞过程,它将底层的 Java NIO API 和操作系统的 TCP 协议栈进行了完美的封装。
要搞清楚底层的 TCP 三次握手是在哪个环节完成的,我们需要把流程分为 Netty 源码层面的执行流 和 操作系统网络栈的交互流 来分析。
一、 Netty 客户端连接核心流程
当我们调用 Bootstrap.connect(host, port) 时,Netty 内部会经历以下几个关键步骤:
1. 初始化与注册 (initAndRegister)
- 创建 Channel: 通过反射创建一个
NioSocketChannel实例。在这个过程中,底层其实调用了 Java NIO 的SocketChannel.open(),并在操作系统中创建了一个 Socket 句柄(此时还未发起连接)。 - 初始化 Channel: 将我们配置的 Handler(如
ChannelInitializer)添加到 Channel 的 Pipeline 中。 - 注册到 EventLoop: 将这个
NioSocketChannel注册到 worker 线程池(NioEventLoopGroup)中的某一个NioEventLoop(即底层的 Selector)上。
2. 发起连接 (doConnect)
- 注册成功后,Netty 会在
NioEventLoop线程中调用底层 Java NIO 的SocketChannel.connect(SocketAddress)方法。 - 注意: 因为 Netty 使用的是非阻塞 I/O,这个
connect()方法通常会立刻返回false(除非是连接本机回环地址,可能瞬间完成)。这代表连接请求已经发送给操作系统,但连接尚未建立。
3. 监听连接完成事件 (OP_CONNECT)
- 既然
connect()返回了 false,Netty 就会将这个 Channel 在 Selector 上注册SelectionKey.OP_CONNECT事件。 - 随后,
NioEventLoop线程继续去干别的事情(比如处理其他 Channel 的读写),不会阻塞等待。
4. 处理连接完成 (finishConnect)
- 当底层的 TCP 连接真正建立成功后,操作系统的网络栈会唤醒 Selector。
NioEventLoop的select()循环会捕获到这个OP_CONNECT事件就绪。- Netty 调用 Java NIO 的
SocketChannel.finishConnect()。如果该方法返回true,说明连接正式建立。 - 最后,Netty 移除
OP_CONNECT监听,注册OP_READ监听,并在 Pipeline 中触发channelActive事件。
二、 底层 TCP 三次握手到底是在哪一步完成的?
结论:三次握手是由操作系统(OS)内核在后台完成的,Netty 只是通过 NIO API 触发了握手的开始,并通过 Selector 接收了握手完成的通知。
具体对应关系如下:
1. 第一次握手(客户端发送 SYN)
- 触发点: 当 Netty 代码执行到
NioSocketChannel.doConnect(),底层调用 Java NIO 的SocketChannel.connect(address)时。 - OS 行为: 操作系统内核收到指令,向服务端发送 TCP
SYN报文。此时 Java 代码不阻塞,直接返回 false。
2. 第二次握手(服务端回复 SYN+ACK)
- 触发点: 服务端收到
SYN后,返回SYN+ACK报文。 - OS 行为: 客户端操作系统的网络协议栈收到该报文,将其放入 TCP 状态机处理。此时 Netty 正在
NioEventLoop中轮询(Selector.select())。
3. 第三次握手(客户端发送 ACK,握手完成)
- 触发点: 客户端操作系统收到
SYN+ACK后。 - OS 行为: 操作系统内核自动回复
ACK报文。此时,在操作系统层面,TCP 三次握手已经完成,该 Socket 的状态变为ESTABLISHED。 - OS 通知 JVM: 操作系统通过 Epoll/Kqueue 等机制,通知 JVM 的 Selector:“这个 Socket 已经可以连接了”。
4. Netty 感知握手完成
- 触发点:
NioEventLoop的Selector.select()收到OP_CONNECT事件就绪。 - Netty 行为: Netty 调用
SocketChannel.finishConnect()。- 这个方法是一个探测方法,用来向操作系统确认连接是否真的成功。
- 当
finishConnect()成功返回true时,在 Netty 的世界里,三次握手才算正式宣告完成。
三、 总结与时间线图解
可以把整个过程想象成点外卖:
- 下单(发 SYN): Netty 调用
connect()。 - 商家接单并做饭(三次握手过程): 操作系统网络栈在后台与服务端进行报文交换。Netty 去忙别的了(非阻塞)。
- 外卖送达通知(OP_CONNECT 就绪): 操作系统告诉 Selector,连接搞定了。
- 开箱确认(finishConnect): Netty 调用
finishConnect()确认无误,开始触发channelActive(可以愉快地收发数据了)。
时序对应图:
plaintext
Netty / Java NIO OS Kernel (TCP Stack) Server
| | |
Bootstrap.connect() | |
| | |
SocketChannel.connect() -------------------> 发送 SYN ----------------------> 收到 SYN
(立即返回 false) | |
| | <----------------------- 回复 SYN+ACK
注册 OP_CONNECT 到 Selector 收到 SYN+ACK |
| | |
Selector.select() 阻塞/轮询等待 ---------------> 发送 ACK (握手在OS层完成) ------> 收到 ACK (连接建立)
| (通知 Epoll/Selector 就绪) |
| | |
OP_CONNECT 事件触发 <-----------------------------| |
| | |
SocketChannel.finishConnect() (返回true) | |
| | |
pipeline.fireChannelActive() | |
(业务层感知连接建立) | |