ZeroMQ socket monitor 事件对照表
ZeroMQ 的 zmq_socket_monitor() 可以为套接字挂一个监控通道,上报连接建立、断开、握手等事件。事件以位掩码表示,而日志和回调里拿到的一般只有十进制数值(例如 Monitor event: 128),不看宏定义很难知道发生了什么。本文给出全部事件的十六进制/十进制对照、event_value 在不同事件下的语义,以及一段日志解析示例。数值已逐一核对 libzmq include/zmq.h(master 分支,2026-10),适用于 4.x 系列。
事件对照表
| 事件宏定义 | 十六进制值 | 十进制值 | 说明 |
|---|---|---|---|
ZMQ_EVENT_CONNECTED | 0x0001 | 1 | 成功建立出站连接(TCP 连接完成) |
ZMQ_EVENT_CONNECT_DELAYED | 0x0002 | 2 | 连接被延迟(例如无可用 I/O 线程) |
ZMQ_EVENT_CONNECT_RETRIED | 0x0004 | 4 | 连接失败后重试(含重试间隔) |
ZMQ_EVENT_LISTENING | 0x0008 | 8 | 成功绑定并开始监听(bind() 成功) |
ZMQ_EVENT_BIND_FAILED | 0x0010 | 16 | 绑定失败(如端口被占用) |
ZMQ_EVENT_ACCEPTED | 0x0020 | 32 | 接受入站连接(TCP accept 成功) |
ZMQ_EVENT_ACCEPT_FAILED | 0x0040 | 64 | 接受连接失败(如资源不足) |
ZMQ_EVENT_CLOSED | 0x0080 | 128 | 套接字成功关闭 |
ZMQ_EVENT_CLOSE_FAILED | 0x0100 | 256 | 套接字关闭失败 |
ZMQ_EVENT_DISCONNECTED | 0x0200 | 512 | 连接断开(对端关闭或网络中断) |
ZMQ_EVENT_MONITOR_STOPPED | 0x0400 | 1024 | 监控被停止(调用 zmq_socket_monitor(..., NULL, ...)) |
ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL | 0x0800 | 2048 | 握手失败,无详细原因(通常为系统级错误) |
ZMQ_EVENT_HANDSHAKE_SUCCEEDED | 0x1000 | 4096 | ZMTP 握手成功(含认证成功) |
ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL | 0x2000 | 8192 | 协议错误 |
ZMQ_EVENT_HANDSHAKE_FAILED_AUTH | 0x4000 | 16384 | 认证失败 |
ZMQ_EVENT_ALL | 0xFFFF | 65535 | 所有事件的掩码(用于启用全部监控) |
每个事件以两帧消息发送:第一帧含 16 位事件号与 32 位事件值,第二帧为受影响 endpoint 的字符串。
event_value 的语义随事件变化
event_value 字段在不同事件中含义不同,排查时要按事件类型解读:
CONNECTED、LISTENING、ACCEPTED→ 文件描述符;CONNECT_RETRIED→ 重试间隔(毫秒);BIND_FAILED、ACCEPT_FAILED、HANDSHAKE_FAILED_NO_DETAIL→errno;HANDSHAKE_FAILED_PROTOCOL→ZMQ_PROTOCOL_ERROR_*之一(见下表);HANDSHAKE_FAILED_AUTH→ ZAP 状态码(300 = 临时拒绝,400 = 无效请求,500 = 内部错误);CLOSE_FAILED仅在 IPC 传输下出现。
协议错误子类型
ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL(8192)的 event_value 取以下值:
| 错误宏定义 | 十六进制值 | 十进制值 | 说明 |
|---|---|---|---|
ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED | 0x10000000 | 268435456 | 未指定的 ZMTP 错误 |
ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND | 0x10000001 | 268435457 | 收到意外的 ZMTP 命令 |
ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE | 0x10000002 | 268435458 | ZMTP 命令序列无效 |
ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE | 0x10000003 | 268435459 | 密钥交换失败(CURVE) |
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO | 0x10000013 | 268435475 | HELLO 命令格式错误 |
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE | 0x10000014 | 268435476 | INITIATE 命令格式错误 |
ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC | 0x11000001 | 285212673 | 加密操作失败(如密钥无效) |
ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH | 0x11000002 | 285212674 | 安全机制不匹配(如一端用 CURVE,另一端未启用) |
ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED | 0x20000000 | 536870912 | ZAP(认证代理)未指定错误 |
ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY | 0x20000001 | 536870913 | ZAP 回复格式错误 |
ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION | 0x20000003 | 536870915 | ZAP 版本不支持 |
ZMQ_PROTOCOL_ERROR_* 的十进制值较大(2.6 亿以上),日志里看到这类数字可直接按前缀识别:0x1xxxxxxx 为 ZMTP 层,0x2xxxxxxx 为 ZAP 层。
日志解析示例
把事件号映射为可读名字,是排查 monitor 相关问题时最常用的手段:
cpp
void handle_monitor_event(uint16_t event, const char *addr) {
static const std::unordered_map<uint16_t, std::string> event_names = {
{1, "CONNECTED"}, {2, "CONNECT_DELAYED"},
{4, "CONNECT_RETRIED"}, {8, "LISTENING"},
{16, "BIND_FAILED"}, {32, "ACCEPTED"},
{64, "ACCEPT_FAILED"}, {128, "CLOSED"},
{256, "CLOSE_FAILED"}, {512, "DISCONNECTED"},
{1024, "MONITOR_STOPPED"},
{2048, "HANDSHAKE_FAILED_NO_DETAIL"},
{4096, "HANDSHAKE_SUCCEEDED"},
{8192, "HANDSHAKE_FAILED_PROTOCOL"},
{16384, "HANDSHAKE_FAILED_AUTH"},
};
auto it = event_names.find(event);
std::string name = (it != event_names.end()) ? it->second : "UNKNOWN";
LOG_DEBUG << "Monitor event: " << event << " (" << name << ") at " << addr;
} 版本注意事项
- 所有事件值均为位掩码,可组合使用(如
ZMQ_EVENT_CONNECTED | ZMQ_EVENT_DISCONNECTED)。 ZMQ_EVENT_ALL(0xFFFF)只涵盖上表前 16 个事件;官方文档同时提醒,随着新事件加入,这个「全集」值会自动包含新事件,依赖固定事件序列的应用不应使用它。- libzmq master 分支已新增
ZMQ_EVENT_PIPES_STATS(0x10000)与ZMQ_EVENT_ALL_V2(ALL_V1 | PIPES_STATS),此时ZMQ_EVENT_ALL不再涵盖全部事件;上表其余值与 4.3.x 一致。
核对依据为 libzmq 仓库 include/zmq.h 与 zmq_socket_monitor(3) 官方文档。