1、背景
Nanomsg 的 usock 模块通过有限状态机(FSM)优雅地管理异步 socket 的生命周期。无论是主动连接的客户端 socket,还是被动接受连接的服务端 socket,它们的状态转换路径既有共性也有差异。本文将分别从客户端和服务端的角度,详细绘制状态转移图并解释每个状态的含义及转换条件。
2、客户端Socket状态机
客户端 socket 指通过 nn_usock_connect 主动向远端发起连接的 socket。其典型生命周期如下:

2.1、状态转移详解
| IDLE | 初始状态,无底层 socket 描述符 | nn_usock_init 后进入 |
| STARTING | 已创建操作系统 socket(socket() 成功),但尚未绑定或连接。可在此设置 socket 选项 | nn_usock_start 触发 NN_FSM_START 动作 |
| CONNECTING | 正在尝试连接远端。可能是同步 connect 返回 EINPROGRESS,也可能是已提交异步任务 | nn_usock_connect 发送 ACTION_CONNECT |
| ACTIVE | 连接建立成功,可以发送和接收数据 | 同步成功或异步连接成功后由 CONNECTING 转入 |
| REMOVING_FD | 正在从 worker 的 poller 中移除 fd 并关闭 socket | ACTIVE 状态下发生错误或收到停止请求 |
| DONE | socket 已关闭,仅作为过渡状态,等待最终停止 | REMOVING_FD 完成,或 STARTING/CONNECTING 中出错 |
2.2、客户端关键转移
1、STARTING 转到 CONNECTING
调用 nn_usock_connect 后,状态机收到 ACTION_CONNECT,立即调用非阻塞 connect
- 若 connect 返回 0,直接发送 ACTION_DONE 转到 ACTIVE
- 若返回 EINPROGRESS,保持在 CONNECTING 并提交 task_connecting 给 worker,等待 fd 可写事件
- 若返回其他错误,发送 ACTION_ERROR 转到 DONE
2、CONNECTING 转到 ACTIVE(异步)
Worker 检测到 fd 可写,触发 NN_WORKER_FD_OUT 事件。状态机调用 getsockopt(SO_ERROR) 检查连接结果。 - 若 SO_ERROR == 0,连接成功,转到 ACTIVE,并通过 task_connected 将 fd 加入 poller(但不设置读写事件),最后上报 NN_USOCK_CONNECTED 给父状态机
- 若 SO_ERROR != 0,连接失败,转到 DONE 并上报 NN_USOCK_ERROR
3、ACTIVE 下的错误处理
在 ACTIVE 状态下,如果 send 或 recv 遇到 ECONNRESET 等致命错误,状态机会收到 ACTION_ERROR,先转到 REMOVING_FD 并提交 task_stop 异步清理资源,最终进入 DONE
3、服务端 Socket 状态机
服务端 socket 分为两类:监听 socket(调用 listen)和被接受 socket(由 accept 产生),下面分别介绍。
3.1、监听 Socket
监听 socket 负责接受新连接,其状态转换如下:
3.1.1、状态转移详解
| IDLE | 初始状态 | |
| STARTING | socket 已创建,尚未监听 | |
| LISTENING | 已调用 listen,可以接受连接,但当前没有正在进行的 accept 操作 | nn_usock_listen 发送 ACTION_LISTEN |
| ACCEPTING | 上层已调用 nn_usock_accept,正在等待新连接(同步尝试失败后进入异步等待) | ISTENING 收到 ACTION_ACCEPT |
| ACCEPTING_ERROR | 因系统资源不足(如文件描述符用尽)导致无法接受新连接,等待上层重试 | ACCEPTING 中 accept 返回 ENFILE/EMFILE 等 |
| CANCELLING | 上层取消等待(例如停止 socket),正在清理 | ACCEPTING 收到 ACTION_CANCEL(由 nn_usock_stop 触发) |
3.1.2、监听 socket 关键转移说明
1、STARTING 到 LISTENING:调用 nn_usock_listen,执行 listen() 系统调用,之后状态机收到 ACTION_LISTEN 进入监听状态
2、LISTENING 到 ACCEPTING:上层调用 nn_usock_accept,发送 ACTION_ACCEPT。状态机先尝试同步 accept:
- 成功获得新 fd 后初始化被接受 socket,发送 ACTION_DONE 回到 LISTENING(一次 accept 完成)
- 失败且错误码为 EAGAIN/EWOULDBLOCK 后,开始 配对 self->asock,提交 task_accept 给 worker,worker 将监听 fd 加入 poller 并关注读事件
- 失败且错误码为资源不足后,开始转入 ACCEPTING_ERROR,上报 NN_USOCK_ACCEPT_ERROR 事件给父状态机
3、ACCEPTING 异步完成:当新连接到达,worker 触发 NN_WORKER_FD_IN,状态机再次调用 accept: - 成功后初始化被接受 socket,解除配对,回到 LISTENING,并分别通知被接受 socket(ACTION_DONE)和上层(NN_USOCK_ACCEPTED)
- 失败且为资源不足后转入 ACCEPTING_ERROR
- ECONNABORTED 则忽略并继续等待
4、ACCEPTING_ERROR 恢复:上层收到错误事件后,可以重新调用 nn_usock_accept(发送 ACTION_ACCEPT)回到 ACCEPTING 状态,重新尝试接受
3.2、被接收Socket
当监听 socket 成功 accept 一个新连接时,会创建一个新的 nn_usock 对象(通常由上层协议分配)。这个被接受的 socket 有自己的状态机路径:

3.2.1、状态详解
| IDLE | 初始状态 |
| BEING_ACCEPTED | 已标记为等待被接受(asock 指向监听 socket),但尚未真正被 accept 系统调用返回 |
| ACCEPTED | 已经被 accept 成功,底层 fd 已存在,但尚未加入 worker 的 poller,可在此设置 socket 选项 |
| ACTIVE | 已激活,可以发送和接收数据 |
3.2.2、被接受 socket 关键转移说明
1、IDLE → BEING_ACCEPTED:在 nn_usock_accept 中,如果 self 处于空闲,会调用 nn_fsm_start 并发送 ACTION_BEING_ACCEPTED,使其进入此状态,同时 self->asock 指向监听 socket
2、 BEING_ACCEPTED → ACCEPTED:当监听 socket 成功执行 accept 后,会调用 nn_usock_init_from_fd 初始化 self 的 fd,然后发送 ACTION_DONE 给 self,转入 ACCEPTED 状态,并向上层上报 NN_USOCK_ACCEPTED 事件
3 、ACCEPTED → ACTIVE:上层(如协议层)在完成必要选项设置后,调用 nn_usock_activate,状态机收到 ACTION_ACTIVATE,将 fd 加入 worker 的 poller,转入 ACTIVE。此后即可进行收发
4、 ACCEPTED 或 ACTIVE 停止:可通过 nn_usock_stop 正常关闭
4、3种socket状态机对比
| 初始创建 | nn_usock_start | nn_usock_start | 由 accept 隐式创建(上层分配) |
| 从 STARTING 转移 | nn_usock_connect 到CONNECTING | nn_usock_listen 到 LISTENING | 无,直接从 IDLE 到 BEING_ACCEPTED |
| 活跃前必经状态 | CONNECTING | LISTENING → ACCEPTING(等待连接) | BEING_ACCEPTED 到ACCEPTED |
| 进入 ACTIVE 的条件 | 连接成功 | 不进入 ACTIVE(监听 socket 不用于数据传输) | 调用 nn_usock_activate |
| 特殊状态 | 无 | ACCEPTING_ERROR、CANCELLING | 无 |
| 典型事件上报 | NN_USOCK_CONNECTED、NN_USOCK_SENT、NN_USOCK_RECEIVED | NN_USOCK_ACCEPTED(来自被接受 socket)、NN_USOCK_ACCEPT_ERROR | NN_USOCK_ACCEPTED、NN_USOCK_SENT、NN_USOCK_RECEIVED |
5、总结
usock 模块通过精细的状态机设计,将异步 socket 的复杂行为分解为清晰的状态和转换。客户端 socket 专注于连接建立和 I/O;服务端监听 socket 专注于接受连接,并容忍临时资源枯竭;被接受 socket 则复用客户端 socket 的 ACTIVE 状态处理数据传输。


