NNG不是简单传std::string.c_str()的玩具库,必须用nng_msg_create分配消息对象、memcpy写入、nng_msg_set_size设长度,否则易致悬空指针、内存泄漏或消息截断。

直接说结论:NNG(Nanomsg Next Gen)在 C++ 中不是“用 std::string + nng_send 就完事”的玩具库,它要求你严格区分 socket 类型、明确消息生命周期、手动管理 nng_msg 对象,并且必须处理 nng_recv 的阻塞/非阻塞语义切换——否则极易出现内存泄漏、消息截断或线程卡死。
为什么不能直接用 nng_send 传 std::string.c_str()
NNG 不接受裸指针 + 长度的松散接口,所有发送必须走 nng_msg 对象。传 c_str() 而不复制内容,会导致 nng_msg 持有悬空指针;若用 nng_msg_alloc 分配后再 memcpy,又容易漏掉 nng_msg_free。正确路径只有一条:
- 用
nng_msg_create分配带所有权的消息对象 - 用
nng_msg_body获取可写内存,memcpy写入数据 - 用
nng_msg_set_size显式设置有效长度(NNG 不自动推断) - 发送后,由 NNG 在内部释放该
nng_msg(除非你用NNG_FLAG_NONBLOCK并检查返回值)
错误示例:nng_send(sock, const_cast<void>(s.data()), s.size(), 0)</void> —— 这是旧版 nanomsg 的用法,NNG 已移除该接口,编译不过。
reqrep 模式下如何避免客户端无限等待响应
reqrep 是请求-应答模式,但 NNG 默认没有超时机制。一旦服务端崩溃或网络中断,nng_recv 会永久阻塞(除非 socket 设为非阻塞)。必须显式配置:
立即学习“C++免费学习笔记(深入)”;
- 创建 socket 后立即调用
nng_setopt_ms(sock, NNG_OPT_RECVTIMEO, 5000)(单位毫秒) - 若
nng_recv返回NNG_ETIMEDOUT,需主动销毁当前nng_msg*并重试或报错 - 注意:
NNG_OPT_SENDTIMEO对req端无效——请求发出后,超时由recv控制 - 服务端
repsocket 不需要设RECVTIMEO,但必须保证每次nng_recv后紧跟nng_send,否则客户端永远等不到响应
漏设 NNG_OPT_RECVTIMEO 是分布式节点上线后“看似正常、实则某次故障就全链路卡死”的最常见原因。
如何安全地跨线程共享一个 nng_socket
NNG socket 本身是线程安全的,但它的底层资源(如 I/O 队列、计时器)依赖于所属的 nng_ctx 或 nng_aio。直接在线程 A 创建、线程 B 调用 nng_send 可以工作,但存在隐患:
- 若 socket 使用了
nng_dial,连接建立过程由后台线程异步完成,线程 B 可能在连接未就绪时就发消息 → 返回NNG_ESTATE - 更稳妥的做法是:用
nng_dial时传入NNG_FLAG_NONBLOCK,再用nng_socket_get_int查询NNG_OPT_STATE是否为NNG_PIPE_STATE_ACTIVE - 或者彻底规避共享:每个线程独占一个 socket,用
nng_pair1或nng_bus做内部线程通信桥接 - 绝对禁止把
nng_aio对象跨线程传递——它绑定到创建它的线程的调度上下文
很多团队踩过坑:用全局 socket 变量 + mutex 保护 nng_send 调用,结果发现 mutex 锁住的是业务逻辑,而 NNG 底层仍在并发触发回调,反而引发死锁。
pipeline 模式中如何识别消息来源节点
pipeline(push/pull)默认不携带元数据,所有消息都是“匿名载荷”。若需知道某条消息来自哪个采集节点(比如 sensor-01 还是 sensor-02),不能靠 IP 或端口推断(NAT、负载均衡会破坏该信息),必须由生产者显式编码:
- 在消息体前加固定长度头(如 8 字节 ID),接收端用
memmove提取 - 或使用
nng_msg_header_append把节点 ID 写入消息 header 区(header 最大 1KB,独立于 body 生命周期) - 切勿用
nng_socket_set_ptr存节点名——那是用于存储用户自定义指针,NNG 不参与管理,极易悬空 - 如果用 TLS 或认证,可开启
NNG_OPT_TLS_AUTH_MODE,让 NNG 自动注入对端证书 CN 字段,但这要求所有节点部署 PKI
header 方案最轻量,但要注意:pull 端调用 nng_recvmsg 后,必须用 nng_msg_header_len 和 nng_msg_header 分离 header,否则直接读 nng_msg_body 会跳过标识字段。



















