0、背景
很多人学习 Nanomsg 源码都会陷入误区:先看 pair.c、reqrep.c 等业务协议代码,看不懂底层的调度根源,始终摸不透「套接字、管道、事件回调」的底层逻辑。
本次我们从根到叶 拆解:以你提供的 protocol.h 内核底层抽象代码为基石,结合 xpair.h、xpair.c、pair.c 三层 Pair 协议实现,彻底打通 Nanomsg 核心架构:
-
protocol.h:Nanomsg 内核通用抽象规范(管道、套接字基类、协议工厂、事件模型),所有业务协议都必须遵守这套规则; -
xpair.h/xpair.c:Pair 协议核心底层实现,对接内核基类,实现一对一连接、管道管理、事件调度; -
pair.c:Pair 协议对外注册入口,完成协议工厂注册、对外暴露协议能力。
读完本文,你将彻底理解:为什么 Pair 是严格一对一?管道和套接字的层级关系?内核如何调度消息收发?Nanomsg 所有协议的通用设计范式。
1、底层基石:protocol.h 核心架构总览
protocol.h 是 Nanomsg 所有上层协议的父类规范 ,定义了两套核心抽象:Pipe 管道通信模型 、Sockbase 套接字基类多态模型,同时定义了协议工厂的注册标准。
1.1 Pipe 管道:传输层与协议层的桥梁
Pipe(管道)是 Nanomsg 最核心的中间层,连接底层传输层(TCP/IPC/INPROC)和上层业务协议(PAIR/REQ/PUB)。所有的网络连接、进程通信连接,最终都会被封装成 Pipe。
1.1.1 管道核心状态与标记
c
#define NN_PIPE_RELEASE 1
#define NN_PIPE_PARSED 2
#define NN_PIPE_IN 33987
#define NN_PIPE_OUT 33988
-
NN_PIPE_RELEASE :管道失效标记,标记当前管道无法收发消息,需要通过上层
in()/out()回调恢复可用状态,是 Nanomsg 连接容错的核心标记; -
NN_PIPE_PARSED:仅用于 INPROC 进程内通信,标记消息已完成头部分离,无需二次解析,提升本机通信效率,TCP/IPC 跨连接传输不生效;
-
NN_PIPE_IN / NN_PIPE_OUT :管道事件,分别代表管道可读、管道可写,由内核触发,回调上层协议的事件处理函数。
1.1.2 管道核心操作 API
内核统一封装管道读写、私有数据绑定、配置获取能力,所有协议通用:
-
nn_pipe_setdata / nn_pipe_getdata:管道绑定协议私有数据,实现管道与上层协议的一一绑定,Pair 协议用它绑定唯一连接会话; -
nn_pipe_send / nn_pipe_recv:底层消息收发核心接口,协议层不直接操作 socket,全部通过管道收发消息; -
nn_pipe_getopt:获取管道、端点的底层配置参数。
核心设计思想 :协议层只关心「管道」,不关心底层是 TCP 还是 IPC,彻底实现协议与传输层解耦。
1.2 sockbase 套接字基类:所有协议的统一父类
nn_sockbase 是 Nanomsg 用 C 语言模拟面向对象的核心,所有套接字协议(PAIR、REQ、PUB、SUB)都继承自该基类,强制统一事件、收发、管道管理规范。
1.2.1 套接字事件标记
c
#define NN_SOCKBASE_EVENT_IN 1
#define NN_SOCKBASE_EVENT_OUT 2
用于向上层应用告知套接字状态:可读、可写,支撑应用层 nn_poll 多路复用。
1.2.2 核心虚函数表(协议必须实现)
nn_sockbase_vfptr 虚函数表,是所有协议的「强制规范」,Pair 协议必须完整实现这套接口:
-
生命周期:
stop(停止套接字)、destroy(销毁资源); -
管道管理(核心):
add/rm(新增/移除连接管道)、in/out(管道可读/可写回调); -
消息调度:
send/recv(协议层消息收发逻辑); -
配置管理:
setopt/getopt(协议私有配置读写); -
状态查询:
events(查询套接字当前读写状态)。
1.3 socktype 协议工厂:协议注册入口
nn_socktype 是 Nanomsg 的协议工厂模型,负责向内核注册协议:协议族、协议标识、收发权限、创建方法、对等端校验。
1.3.1 协议权限标记
c
#define NN_SOCKTYPE_FLAG_NORECV 1 // 禁止接收
#define NN_SOCKTYPE_FLAG_NOSEND 2 // 禁止发送
Pair 协议无任何收发限制,因此无该标记,支持双向自由收发。
1.3.2 工厂核心能力
每个协议必须实现:create(创建协议实例)、ispeer(校验对端协议合法性),比如 Pair 只允许对端也是 Pair 协议,拒绝其他协议连接。
2、xpair.h:Pair 协议子类继承与定义
基于 protocol.h 的内核规范,xpair.h 定义了 Pair 专属结构体,完成基类继承 + 业务字段扩展。
c
struct nn_xpair {
/* 继承套接字基类,拥有所有内核统一能力 */
struct nn_sockbase sockbase;
/* Pair 核心:唯一管道,严格一对一的根源 */
struct nn_pipe *pipe;
};
2.1 极简设计的核心奥义
相比于 REQ/REP 的状态机、PUB/SUB 的订阅链表,Pair 结构体极度精简,只有一个管道指针:
-
仅保存唯一一条连接管道,天然限制多连接接入;
-
无消息队列、无状态机、无路由表,纯粹透传双向消息;
-
完全遵循
nn_sockbase基类规范,适配内核所有调度逻辑。
3、xpair.c:Pair 核心业务逻辑实现
xpair.c 是 Pair 协议的核心实现,严格实现protocol.h 规定的所有虚函数,落地一对一通信逻辑。
3.1 管道管理:实现严格一对一限制
3.1.1 add 新增管道(连接建立回调)
当底层 TCP/IPC 连接建立,内核触发add 回调:
-
判断当前 Pair 是否已经绑定管道;
-
已有连接则直接拒绝新管道,保证全局唯一连接;
-
无连接则绑定当前管道,完成一对一映射。
这就是 Pair 只能一对一的底层根源,内核层面直接限制多连接,而非业务层限制。
3.1.2 rm 移除管道(连接断开回调)
当连接断开,内核触发 rm 回调:
-
清空 Pair 绑定的管道指针;
-
重置状态,允许后续新的一对一连接接入;
-
无残留资源,无需复杂回收逻辑。
3.1.3 in/out 管道事件回调
完全遵循 protocol.h 管道事件规范:
-
in回调:管道可读,唤醒上层应用接收消息; -
out回调:管道可写,唤醒上层应用发送消息; -
双向事件独立触发,无收发顺序限制,完美适配 Pair 双向通信特性。
3.2 消息收发:无限制透明透传
3.2.1 send 发送逻辑
-
校验当前是否存在有效管道连接;
-
直接调用内核
nn_pipe_send透传消息; -
无消息丢弃、无路由筛选、无应答等待,发送即透传。
3.2.2 recv 接收逻辑
-
校验管道有效性;
-
调用内核
nn_pipe_recv读取对端消息; -
无消息排序、无订阅过滤,纯原生接收。
3.3 状态查询与生命周期
-
events:根据管道读写状态,向上层返回NN_SOCKBASE_EVENT_IN/OUT,支撑多路复用; -
stop/destroy:遵循基类规范,逐层释放管道资源、套接字资源,无内存泄漏; -
setopt/getopt:复用内核通用配置,无私有配置,保持极简。
4、pair.c:协议工厂注册入口
pair.c 极其精简,唯一作用:将 Pair 协议注册到 Nanomsg 内核协议工厂 ,对应 protocol.h 的 nn_socktype 规范。
4.1 协议注册核心逻辑
c
struct nn_socktype nn_pair_socktype = {
AF_SP,
NN_PAIR,
0, // 无收发限制,双向全双工
nn_xpair_create,
nn_xpair_ispeer
};
-
协议族为标准 SP 协议族,协议标识为 NN_PAIR;
-
标记位为 0,无
NORECV/NOSEND限制,支持双向自由收发; -
绑定创建函数
nn_xpair_create、对等校验函数nn_xpair_ispeer; -
内核启动时加载该结构体,完成 Pair 协议的全局注册,应用层即可通过
nn_socket(AF_SP, NN_PAIR)创建套接字。
4.2 对等端校验 ispeer
严格遵循内核规范,只允许 Pair 协议互相连接,拒绝所有其他协议(REQ、PUB 等)的接入,保证通信协议一致性。
5、四层架构闭环:从内核规范到业务落地
结合 protocol.h 底层规范 + Pair 三层实现,完整梳理 Nanomsg Pair 通信全链路:
5.1 完整层级关系
-
内核抽象层(protocol.h):定义管道、套接字基类、协议工厂的统一规范,是所有协议的宪法;
-
协议实现层(xpair.h/xpair.c):遵守内核规范,实现 Pair 一对一、双向透传的业务逻辑;
-
协议注册层(pair.c):将 Pair 协议纳入内核管理,对外开放调用入口;
-
传输层(IPC/TCP/usock):底层负责字节流传输,对上层协议完全透明。
5.2 核心运行流程
-
应用创建 Pair 套接字 → 内核通过
nn_socktype工厂创建nn_xpair实例; -
套接字 bind/connect → 底层建立 TCP/IPC 连接,封装为 Pipe 管道;
-
内核触发
add回调 → Pair 绑定唯一管道,拒绝多连接; -
管道可读/可写 → 触发
in/out事件,唤醒应用收发消息; -
连接断开 → 触发
rm回调,清空管道,重置套接字状态。
6、Pair 协议核心设计总结
依托 protocol.h 的内核抽象设计,Pair 协议做到了极致精简、高可靠、高实时:
-
一对一强制约束:由内核管道绑定机制实现,无业务容错漏洞;
-
全双工自由通信:无收发顺序、无状态限制,是纯粹的双向透明通道;
-
完全解耦架构:协议层与传输层隔离,一套 Pair 逻辑兼容 TCP/IPC/INPROC 所有传输方式;
-
规范统一:完全遵循内核 sockbase 多态规范,和 REQ/PUB/SUB 共用一套内核调度体系;
-
资源极简:无队列、无状态机、无复杂调度,内存开销极低,适合高频一对一通信。
7、和其他协议的本质区别
看懂 protocol.h 内核规范后,就能理解所有 Nanomsg 协议的差异根源:
-
Pair:单管道、无路由、无状态、双向透传;
-
REQ/REP:基于 sockbase 扩展状态机,强制问答收发顺序;
-
PUB/SUB:基于 sockbase 扩展订阅链表,支持多连接消息广播;
所有协议的底层内核骨架完全一致,差异仅在业务层对虚函数的实现逻辑,这就是 Nanomsg 架构的精髓。