zmq::context_t必须全局复用,线程安全但socket_t不安全;req/rep需严格send/recv交替;pub/sub无历史消息缓存;链接时-lzmq须置于源文件后。

zmq::context_t 必须全局复用,不能每个线程都 new 一个
ZeroMQ 的上下文不是“每次通信新建一个”的东西——它是整个进程的资源管理器,内部维护 I/O 线程、连接池和内存池。如果在每个函数里写 zmq::context_t ctx(1),会导致句柄泄漏、TCP 连接数暴涨,甚至触发系统级 Too many open files 错误。
- 正确做法:整个程序生命周期只创建一次,推荐用静态局部变量或全局智能指针管理
- 多线程场景下,
zmq::context_t是线程安全的,但zmq::socket_t不是——每个线程必须用自己的 socket - 别信“开多个 context 能提高吞吐”,实测反而降低性能,因为 I/O 线程竞争加剧
REQ/REP 模式下 send/recv 顺序错一丁点就卡死
这是 C++ 开发者踩得最多的一类坑:ZMQ_REQ 和 ZMQ_REP 强制要求严格交替调用,客户端必须先 send() 再 recv(),服务端必须先 recv() 再 send()。顺序颠倒不会报错,而是直接阻塞,且无超时提示。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 常见错误现象:客户端发完消息后
recv()一直不返回;服务端recv()成功但后续send()失败(其实是 socket 已进入“等待新请求”状态) - 调试技巧:加
zmq::send_flags::dontwait或ZMQ_SNDTIMEO/ZMQ_RCVTIMEO套接字选项,让阻塞变成可捕获的异常 - 注意:
ZMQ_REP不允许连续两次send(),哪怕中间没recv()—— 它的设计就是“一问一答”闭环
PUB/SUB 订阅者收不到启动前的消息,不是 bug 是设计
ZMQ_PUB 不做消息缓存,ZMQ_SUB 也不回溯历史。只要订阅者还没调用 connect() 并完成 TCP 握手,之前发布的所有消息全部丢弃。这不是丢包,是 ZeroMQ 明确规定的语义。
- 典型误判:服务端先跑,客户端后起,结果第一条消息永远收不到 → 实际上是预期行为
- 解决思路:发布端加简单握手机制(比如先发一条
"HELLO",等收到 SUB 端确认再开始业务消息) - 主题过滤靠
setsockopt(ZMQ_SUBSCRIBE, ...),空字符串""表示接收全部;二进制主题需注意首字节是否为\0(会被截断) - IPC 场景下,SUB 端
connect()前要确保 PUB 端已bind()且 socket 文件存在,否则会静默失败
编译链接顺序写反了,-lzmq 放源文件前面就找不到符号
g++ 链接阶段是单向扫描的,库必须放在它所服务的目标文件之后。如果写成 g++ -lzmq app.cpp -o app,链接器在看到 -lzmq 时还不知道哪些符号要解析,结果就是一堆 undefined reference to zmq_*。
- 正确命令:必须是
g++ app.cpp -o app -lzmq - 依赖检查:运行前用
ldd app确认是否链接到 libzmq.so;若提示not found,说明LD_LIBRARY_PATH没配或sudo ldconfig没刷新缓存 - 头文件路径:cppzmq 是纯头文件库,只要
#include <zmq.hpp></zmq.hpp>可见即可,无需额外链接;但务必确认它版本与 libzmq 兼容(例如 libzmq 4.3.x 对应 cppzmq 4.9+)
send() recv(),而是 context 生命周期、socket 状态机和链接时序这三件事卡在一起出问题。尤其在热更新或子进程场景下,close() 没调对,下次 bind 就报 Address already in use——这时候别急着改代码,先看 lsof -i :5555。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










