C++之ZeroMQ的使用

zeromq官網的使用教程很詳細,這裏主要介紹兩種常使用的模式。

  1. 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);
        }
    }
}

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章