git clone https://github.com/zeromq/cppzmq.git
cd cppzmq # 切稳定匹配版本,libzmq4.3.5 搭配 v4.10.0 / v4.11.0
git checkout v4.10.0
./configure --enable-debug
cmake -B build -DCMAKE_INSTALL_PREFIX=`pwd`/output
git clone https://github.com/zeromq/cppzmq.git
//demo
#include <zmq.h>
#include "zmq.hpp"
#include <iostream>
#include <vector>
#include <signal.h>
#include <cstring>
int main()
{
zmq::context_t ctx(1);
std::vector<std::string> endpoints = {
"tcp://127.0.0.1:5555",
"ipc:///tmp/zmq_pub_ipc"
};
std::vector<zmq::socket_t> subs;
for (const auto& addr : endpoints)
{
zmq::socket_t sub(ctx, zmq::socket_type::sub);
sub.set(zmq::sockopt::subscribe, "");
sub.set(zmq::sockopt::reconnect_ivl, 1000); // 5秒重试间隔
sub.set(zmq::sockopt::reconnect_ivl_max, 1000); // 最大30秒
sub.connect(addr);
subs.emplace_back(std::move(sub));
}
std::vector<zmq_pollitem_t> items(subs.size());
for (size_t i = 0; i < subs.size(); i++)
{
itemsi.socket = subsi.handle();
itemsi.fd = 0;
itemsi.events = ZMQ_POLLIN;
itemsi.revents = 0;
}
while (true)
{
// 2000毫秒超时,替代ppoll
int ret = zmq_poll(items.data(), static_cast<int>(items.size()), -1);
if (ret < 0)
{
int err = zmq_errno();
std::cerr << "poll err: " << zmq_strerror(err) << "\n";
if (err == ETERM) break;
// poll被信号打断就重试
if (err == EINTR) continue;
continue;
}
if (ret == 0) continue;
for (size_t i = 0; i < items.size(); i++)
{
if (itemsi.revents & ZMQ_POLLIN)
{
zmq::message_t msg;
auto res = subsi.recv(msg);
if (!res.has_value())
continue;
std::string data(msg.data<char>(), msg.size());
std::cout << "" \<\< endpoints\[i << "] recv: " << data << "\n";
itemsi.revents = 0;
}
}
}
return 0;
}
/////////////
htop -p `pidof ser1`
1.查看到主线程有0.7的cpu
//2s超时一次的开销
int ret = zmq_poll(items.data(), static_cast<int>(items.size()), 2000);
//改成这个无任何开销 就是会一直阻塞不好退出这个程序
int ret = zmq_poll(items.data(), static_cast<int>(items.size()), -1);
2.查看ZMGbg/IO/0线程有0.4的开销
用strace -p 查看在不停的尝试连接
将尝试连接的间隔拉开
sub.set(zmq::sockopt::reconnect_ivl, 1000); // 1秒重试间隔
sub.set(zmq::sockopt::reconnect_ivl_max, 1000); // 最大1秒

import zmq
import time
def main():
初始化上下文
context = zmq.Context()
创建发布套接字
pub = context.socket(zmq.PUB)
同时绑定TCP和IPC两个通道
pub.bind("tcp://127.0.0.1:5555")
pub.bind("ipc:///tmp/zmq_pub_ipc")
print("发布服务启动成功")
print("绑定TCP: tcp://127.0.0.1:5555")
print("绑定IPC: ipc:///tmp/zmq_pub_ipc")
print("-" * 40)
num = 0
while True:
组装消息内容
msg = f"消息序号:{num} | 实时测试数据"
广播发送,TCP、IPC所有订阅者都会收到这条消息
pub.send_string(msg)
print(f"已发送:{msg}")
num += 1
time.sleep(0.1)
if name == "main":
try:
main()
except KeyboardInterrupt:
print("\n检测到退出信号,关闭服务")
finally:
释放资源
zmq.Context().destroy()
10HZ的频率发布数据.

发二进制
import zmq
import time
def sleep_ms(ms):
return time.sleep(ms / 1000.0)
def main():
ctx = zmq.Context()
三个独立发布socket
pub_tcp = ctx.socket(zmq.PUB)
pub_tcp.bind("tcp://127.0.0.1:5555")
pub_ipc = ctx.socket(zmq.PUB)
pub_ipc.bind("ipc:///tmp/zmq_pub_ipc")
cnt = 0
while True:
构造100字节填充数据
buf = bytearray(100) # 初始全0,固定长度100
可自定义填充内容:前4字节放序号,剩余填充数据
buf0:4 = cnt.to_bytes(4, byteorder='little', signed=False)
示例:中间填充自定义值
for i in range(4, 100):
bufi = (cnt + i) % 256
data_100bytes = bytes(buf) # 转为bytes,严格100长度
assert len(data_100bytes) == 100
分别发给不同通道
#pub_tcp.send(data_100bytes)
pub_ipc.send(data_100bytes)
print(f"发送第{cnt}包,长度:{len(data_100bytes)} bytes")
cnt += 1
sleep_ms(10)
if name == "main":
try:
main()
except KeyboardInterrupt:
print("\n退出")
finally:
ctx.destroy()
////////////////

查看每个fd分别是啥
LD_LIBRARY_PATH=/home/ubuntu22/lib/libzmq/build/output/lib gdb --args ./ser1
catch syscall epoll_create1
catch syscall eventfd2
这里每个调用栈eventfd2会中断2次 一次是进入中断 一次是退出中断,退出的时候
(gdb) info reg rax
rax 0x3
第一个eventfd 3是ctx本身自带的mailbox 里面 里面带了signaler 里面带了fd.

zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)
->start()
->...
第二个是socket里面reaper里面带的fd4

然后这时reaper里面创建了eventpoll 也就是fd5
接下来是io_thread_t里面的eventfd fd6

然后是io_therad里面创建的eventpoll fd7
zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)
->socket_base_t *s = socket_base_t::create (type_, this, slot, sid);
这次是socket_base里面的_mailbox fd8
// Socket's mailbox object.
i_mailbox *_mailbox;
这里循环有2个 zmq::socket_t sub(ctx, zmq::socket_type::sub);
所以下一个是socket_base里面的_mailbox fd9
接下来就是work线程里面的两个socket fd

/////////plug 触发epoll
