基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

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。
  • NioEventLoopselect() 循环会捕获到这个 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 感知握手完成

  • 触发点: NioEventLoopSelector.select() 收到 OP_CONNECT 事件就绪。
  • Netty 行为: Netty 调用 SocketChannel.finishConnect()
    • 这个方法是一个探测方法,用来向操作系统确认连接是否真的成功。
    • finishConnect() 成功返回 true 时,在 Netty 的世界里,三次握手才算正式宣告完成。

三、 总结与时间线图解

可以把整个过程想象成点外卖:

  1. 下单(发 SYN): Netty 调用 connect()
  2. 商家接单并做饭(三次握手过程): 操作系统网络栈在后台与服务端进行报文交换。Netty 去忙别的了(非阻塞)。
  3. 外卖送达通知(OP_CONNECT 就绪): 操作系统告诉 Selector,连接搞定了。
  4. 开箱确认(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()                     |                               |
 (业务层感知连接建立)                               |                               |
00:00
00:00