ZeroMQ在C++中需显式管理上下文与套接字:zmq::context_t ctx{1}创建I/O线程,zmq::socket_t sock(ctx, ZMQ_PUB)构造套接字,必须设置HWM、订阅空字符串、避免连接时序错误,并严格按socket先close、context后destroy顺序安全关闭。

ZeroMQ 在 C++ 中无法直接“搭建架构”,它只提供通信原语;所谓发布订阅(pub/sub)和管道(push/pull)必须靠你显式绑定端点、管理套接字生命周期、处理消息边界与序列化——否则连最基础的丢消息或阻塞都避不开。
怎么初始化 ZeroMQ 上下文和套接字
ZeroMQ 不是传统 socket 库,zmq::context_t 是资源中心,每个进程通常只建一个;套接字(zmq::socket_t)必须在上下文创建后构造,并显式设置关键选项。
-
zmq::context_t ctx{1}:参数是 I/O 线程数,设为 1 足够轻量,设为 0 会触发未定义行为 - 发布端用
ZMQ_PUB,订阅端用ZMQ_SUB;管道通信必须配对使用ZMQ_PUSH+ZMQ_PULL,混用会静默失败 - 必须调用
socket.set(zmq::sockopt::rcvhwm, 1000)或sndhwm,否则默认 HWM=1000,发太快会丢包且无提示 - 订阅端需调用
socket.set(zmq::sockopt::subscribe, ""_zmq_string)(空字符串表示订阅所有主题),不设就收不到任何消息
为什么 pub/sub 架构里订阅端总收不到消息
ZeroMQ 的 ZMQ_PUB 套接字不缓存历史消息,连接建立前发出的消息永远丢失;这是设计使然,不是 bug。常见误操作包括:
- 先启动 subscriber 再启动 publisher:subscriber 连接时 publisher 尚未 bind,但 zmq 会自动重连,问题不大
- publisher 先 bind 后立刻 send:此时 subscriber 还没 connect 完,第一批消息大概率被丢弃
- 没加
std::this_thread::sleep_for(1ms)或用同步信号(如 REQ/REP 握手)等待连接就绪 - 用了
tcp://*但没指定具体 IP,例如tcp://*:5555在多网卡机器上可能 bind 到错误接口
实操建议:publisher 启动后 sleep 10–100ms,或让 subscriber 主动发一个 "READY" 请求,publisher 收到后再开始广播。
立即学习“C++免费学习笔记(深入)”;
push/pull 管道如何避免 worker 饿死或积压
ZMQ_PUSH 默认负载均衡分发,ZMQ_PULL 按照接收顺序处理;但若某个 worker 处理慢,后续消息会在其 socket 接收缓冲区堆积,直到达到 rcvhwm 触发阻塞(对 PUSH 端)或丢弃(取决于 zmq::sockopt::sndhwm 和 zmq::sockopt::linger)。
- worker 启动时应先
connect到 push 端,而非 push 端bind后等 worker —— 否则 push 端无法感知 worker 数量,负载不均 - 务必设置
socket.set(zmq::sockopt::linger, 0),否则程序退出时 linger 默认 1s,可能卡住 - 不要用
send(..., zmq::send_flags::dontwait)忽略阻塞,而应监控zmq::poll超时或用zmq::recv配合zmq::recv_flags::dontwait做非阻塞轮询 - 消息体建议用长度前缀(4 字节 big-endian)+ payload,ZeroMQ 不保证消息边界,
send多次不等于recv多次
怎么安全关闭 ZeroMQ 套接字和上下文
ZeroMQ 关闭顺序很关键:必须先 close() 所有 zmq::socket_t,再 context.destroy();反过来会导致未定义行为,常见现象是进程 hang 在析构。
- 用 RAII 封装 socket?小心:C++11 后
zmq::socket_t移动构造可行,但 move 后原对象变为无效状态,不可再调用close() - 多线程中,一个线程
close()socket 后,其他线程继续send会触发 SIGSEGV —— ZeroMQ 不做运行时检查 -
linger=0只影响 close 行为,不影响 send/receive;若想强制丢弃未发送消息,需在 close 前手动调用socket.set(zmq::sockopt::sndhwm, 0)并等待 - 调试时可启用
zmq::ctx_set(ctx, ZMQ_CTX_MAX_SOCKETS, 1024)防止默认限制 1024 导致新建 socket 失败
最易被忽略的是:ZeroMQ 的 zmq::message_t 持有内存,如果用 data() 取指针后忘了 rebuild() 或没 copy,下次 send 可能发错内容 —— 它不是 std::string,没有隐式拷贝语义。


















