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 這個很容易搞混,另外,就是訂閱的時候對消息的過濾條件要寫對。