c++进程间通信--zeromq

ZeroMQ(也写作 ØMQ、0MQ 或 zmq)是一个高性能的异步消息队列库,提供了一种灵活的方式来实现进程间通信(IPC)以及网络通信。在C++中使用ZeroMQ进行进程间通信非常直观和高效。zmq的通信模式如下:

下面先说明请求响应模式的特点和建立过程。

1.特点

REQ-REP模式是一种同步的、基于请求-响应的通信方式,适用于需要明确请求和响应的场景,比如RPC(远程过程调用)。

2.建立过程

请求响应模式(Request-Reply Pattern)是ZeroMQ提供的一个基本通信模式,用于实现客户端与服务器之间的同步交互,其中客户端发送请求并等待服务器的响应。下面是该模式的详细工作过程:

2.1. 初始化套接字

服务器端(REP):创建并绑定一个ZMQ_REP类型的套接字到一个指定的地址(可以是TCP端口或IPC路径)。

void *context = zmq_ctx_new();
void *socket = zmq_socket(context, ZMQ_REP);
int rc = zmq_bind(socket, "tcp://*:5555"); // 绑定到端口555

客户端(REQ):创建并连接一个ZMQ_REQ类型的套接字到服务器的地址。

void *context = zmq_ctx_new();
void *socket = zmq_socket(context, ZMQ_REQ);
int rc = zmq_connect(socket, "tcp://localhost:5555"); // 连接到服务器的5555端口

2.2. 请求发送和响应接收

客户端(REQ):

发送请求:客户端通过zmq_send()发送一个请求消息到服务器。

zmq_send(socket, "Request Data", strlen("Request Data"), 0);

等待响应:客户端调用zmq_recv()阻塞等待服务器的响应。

char buffer[1024];
zmq_recv(socket, buffer, 1024, 0);
std::cout << "Received: " << buffer << std::endl;

服务器端(REP)

接收请求:服务器通过zmq_recv()阻塞等待客户端的请求。

char buffer[1024];
zmq_recv(socket, buffer, 1024, 0);
std::cout << "Received Request: " << buffer << std::endl;

处理请求并发送响应:服服务器处理请求后,通过zmq_send()发送响应消息给客户端。

zmq_send(socket, "Response Data", strlen("Response Data"), 0);

3.顺序和匹配

ZeroMQ确保请求与响应的顺序对应,即客户端发送的每个请求都将对应于从服务器接收的下一个响应。

4.几点说明

  • 同步性:请求响应模式是同步的,客户端在接收到响应前会阻塞。

  • REP套接字行为:服务器的REP套接字在发送完响应后会自动进入等待下一个请求的状态,不允许连续发送两个响应而没有收到新的请求。

  • 错误处理:如果任何一方关闭连接,另一方在尝试读写时可能会遇到错误,需要适当处理。

5.完整sample

5.1 linux zmq安装:

sudo apt-get install libzmq3-dev
sudo apt-get install czmq-dev
git clone https://github.com/zeromq/cppzmq.git
cd cppzmq
mkdir build && cd build
cmake ..
make
sudo make install

5.2 服务器

#include <zmq.hpp>
#include <iostream>
#include <string>

int main() {
    zmq::context_t context(1);
    zmq::socket_t socket(context, ZMQ_REP);
    socket.bind("tcp://*:5555");

    while (true) {
        zmq::message_t request;
        socket.recv(&request);
        std::string request_str = std::string(static_cast<char*>(request.data()), request.size());
        std::cout << "Received request: " << request_str << std::endl;

        std::string response = "Response: " + request_str;
        zmq::message_t reply(response.size());
        memcpy((void*)reply.data(), response.c_str(), response.size());
        socket.send(reply);
    }

    return 0;
}

5.3 客户端

#include <zmq.hpp>
#include <iostream>
#include <string>

int main() {
    zmq::context_t context(1);
    zmq::socket_t socket(context, ZMQ_REQ);
    socket.connect("tcp://localhost:5555");

    for (int request_nbr = 0; request_nbr != 5; ++request_nbr) {
        zmq::message_t request(std::to_string(request_nbr));
        socket.send(request, ZMQ_NOBLOCK);
        
        zmq::message_t reply;
        socket.recv(&reply);
        std::string reply_str = std::string(static_cast<char*>(reply.data()), reply.size());
        std::cout << "Received reply " << request_nbr << ": " << reply_str << std::endl;
    }

    return 0;
}

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

下面说明下zmq发布订阅方式的建立过程。

1.发布订阅模式

ZeroMQ的发布订阅(Pub-Sub)模式是一种一对多的消息传递模式,适用于消息广播场景。在这个模式中,一个或多个发布者(Publisher)发送消息到特定的主题上,而一个或多个订阅者(Subscriber)可以选择订阅这些主题以接收消息。

2.发布订阅模式建立过程

2.1. 初始化Context

在ZeroMQ中,所有的套接字都关联到一个唯一的上下文(Context)。首先,创建一个上下文对象:

#include <zmq.hpp>

zmq::context_t context(1); // 参数1表示Io线程的数量,默认为1

2.2. 创建并配置发布者套接字

发布者创建一个ZMQ_PUB类型的套接字,并将其绑定到一个地址。这个地址可以是TCP端口或IPC通道,允许订阅者连接。

zmq::socket_t publisher(context, ZMQ_PUB);
publisher.bind("tcp://*:5555"); // 绑定到所有可用IP的5555端口
// 或使用IPC
// publisher.bind("ipc:///tmp/pubsub.ipc");

2.3. 创建并配置订阅者套接字

  • 订阅者创建一个ZMQ_SUB类型的套接字,并连接到发布者的地址。

  • 订阅者还需要订阅至少一个主题,如果不订阅任何主题,默认情况下不会接收到任何消息。主题可以是空字符串,表示订阅所有主题。

zmq::socket_t subscriber(context, ZMQ_SUB);
subscriber.connect("tcp://localhost:5555"); // 连接到发布者的5555端口
// 或使用IPC
// subscriber.connect("ipc:///tmp/pubsub.ipc");

// 订阅主题,这里订阅名为"weather"的主题
subscriber.setsockopt(ZMQ_SUBSCRIBE, "weather", strlen("weather"));
// 若要订阅所有主题,可以设置空字符串
// subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);

2.4.发布消息

发布者通过zmq_send()或相应的ZeroMQ API发送消息到一个主题。消息由主题和数据组成,主题通常是消息的前缀。

std::string topic = "weather";
std::string data = "It's sunny today.";
zmq::message_t msg(data.size());
memcpy(msg.data(), data.data(), data.size());
publisher.send(msg, ZMQ_SNDMORE); // 发送主题
publisher.send(topic);

注意:在某些版本的ZeroMQ中,可能需要手动拼接主题和消息内容,或者使用特定的API来一次性发送带有主题的消息。

2.5.接收消息

订阅者使用zmq_recv()或相应的API接收消息。ZeroMQ会自动过滤掉未订阅主题的消息。

zmq::message_t received;
subscriber.recv(&received);
std::string receivedData(reinterpret_cast<char*>(received.data()), received.size());
std::cout << "Received: " << receivedData << std::endl;

3.总结

发布订阅模式在ZeroMQ中的建立过程涉及创建上下文、发布者和订阅者套接字,绑定和连接到指定的地址,设置订阅主题,以及发送和接收消息。这种模式支持动态添加和移除订阅者,且发布者无需了解订阅者的具体信息,提高了系统的可扩展性和灵活性。附录:完整Demo

发布者

#include <zmq.hpp>
#include <iostream>
#include <string>
#include <unistd.h> // for sleep

int main() {
    zmq::context_t context(1);
    zmq::socket_t publisher(context, ZMQ_PUB);
    publisher.bind("tcp://*:5555"); // 绑定到所有可用IP的5555端口

    std::cout << "Publisher is up and running..." << std::endl;

    while (true) {
        std::string message = "Current temperature: 22 C";
        std::string topic = "weather";

        // 发布消息,ZeroMQ不直接支持在消息前加主题,需手动拼接
        zmq::message_t msg(topic.size() + message.size() + 1); // +1是为了添加分隔符
        memcpy(msg.data(), topic.data(), topic.size());
        memcpy(static_cast<char*>(msg.data()) + topic.size() + 1, message.data(), message.size()); // +1跳过分隔符位置
        publisher.send(msg, ZMQ_DONTWAIT);

        std::cout << "Sent: " << message << std::endl;
        usleep(1000000); // 每秒发送一次
    }

    return 0;
}

订阅者

#include <zmq.hpp>
#include <iostream>
#include <string>

int main() {
    zmq::context_t context(1);
    zmq::socket_t subscriber(context, ZMQ_SUB);

    subscriber.connect("tcp://localhost:5555"); // 连接到发布者的5555端口
    subscriber.setsockopt(ZMQ_SUBSCRIBE, "weather", 7); // 订阅主题"weather"

    std::cout << "Subscriber is connected and subscribed to 'weather'." << std::endl;

    while (true) {
        zmq::message_t message;
        subscriber.recv(&message);

        // 假设消息格式为 主题+分隔符+内容,这里直接处理消息内容
        std::string received = std::string(static_cast<char*>(message.data()));
        std::cout << "Received: " << received.substr(8) << std::endl; // 去掉前8个字符("weather ")
    }

    return 0;
}

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值