zeromq官网的使用教程很详细,这里主要介绍两种常使用的模式。
- Request/Reply (请求应答模式)
这种模式和网络API请求一样,先向一地址的发送请求,然后等待服务器返回对应的数据或者状态,服务器在后端一直监听发过来的请求,然后返回对应的数据即可。
这里代码都是cppzmq的封装的。
void sendMsg()
{
int port=5555;
std::cout << "send message ..." << std::endl;
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_REQ);
socket.connect ("tcp://192.168.0.110:" + std::to_string(port)); // 这里选择connect 具体的地址和端口号
std::string str = "1001"; // 发送的内容
zmq::message_t msg(str.size());
memcpy(msg.data(), str.data(), str.size());
bool ok = socket.send(msg);
if(ok){
std::cout << "send success" << std::endl;
}else {
std::cout << "send failed" << std::endl;
}
std::cout << "send message finished" << std::endl;
// 等待服务器返回的响应
zmq::message_t reply;
socket.recv (&reply);
std::cout << reply.size() << std::endl;
char* buffer=new char[reply.size()];
memcpy(buffer, reply.data(), reply.size());
std::cout << std::string(buffer) << std::endl;
delete[] buffer;
}
服务器端代码:
void receiveMsg()
{
zmq::context_t ctx;
zmq::socket_t socket (ctx, ZMQ_REP);
socket.bind ("tcp://*:5555"); // 监听所有的IP地址
while (true) {
zmq::message_t request;
bool ok = socket.recv(&request);
if(ok){
char* buffer = new char[request.size()];
memcpy(buffer , request.data (), request.size());
std::cout << "receive:" << std::string(buffer)<< std::endl;
delete[] buffer
zmq::message_t reply (2);
memcpy (reply.data (), "ok", 2);
socket.send (reply);
}
}
}
- publish/subscribe (发布订阅模式)
这种模式类似于广播,当发布者发布信息后,只有订阅者订阅了才能收到信息。有时候应用中会用到视频流信息,这样的话,可以采用该模式,发布者不断的发布图片,而订阅者去订阅地址即可。
上代码,代码中用到了OpenCV图像库,可以对图像进行编码和解码
void pubImage()
{
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_PUB);
socket.bind("tcp://192.168.0.100:5555"); // 这里是绑定地址
cv::VideoCapture cap(0); // 打开摄像头
cv::Mat frame;
std::vector<uchar> data_encode;
while (true) {
data_encode.clear();
cap.read(frame);
cv::imencode(".jpg", frame, data_encode); // 编码图片
std::cout<< "imencode" << std::endl;
zmq::message_t msg(data_encode.size());
memcpy(msg.data(), data_encode.data(), data_encode.size());
bool ok = socket.send(msg); // 发送数据
if(!ok){
std::cout<< "send failed" << std::endl;
}else{
std::cout<< "send ok" << std::endl;
}
}
}
订阅端
void subscribe()
{
zmq::context_t ctx(1);
zmq::socket_t socket (ctx, ZMQ_SUB);
socket.connect ("tcp://192.168.0.100:5555");
socket.setsockopt(ZMQ_SUBSCRIBE, nullptr, 0); //这里是需要设置channel,也就是发送的数据的开头,这里设置为nullptr,长度为0 意思是对接收的消息不过滤,如果设置比如socket.setsockopt(ZMQ_SUBSCRIBE, "AB", 2) 只接收以AB开头的消息,2为AB的长度。
std::cout << "subscribe" << std::endl;
char key;
cv::Mat img_decode;
while (true) {
std::cout <<"subscribe ..." << std::endl;
zmq::message_t request;
auto ok = socket.recv(&request);
std::vector<uchar> data_reply;
if(ok){
data_reply.resize(request.size());
memcpy (data_reply.data(), request.data (), request.size());
img_decode = cv::imdecode(data_reply, cv::IMREAD_COLOR);
cv::imshow("img_decode", img_decode);
if((char)cv::waitKey(1) == 'q'){
cv::imwrite("D://1.jpg", img_decode);
};
}
}
}
总结: 在使用的时候一定要搞清楚,哪个是bind,哪个是connect 这个很容易搞混,另外,就是订阅的时候对消息的过滤条件要写对。