ZeroMQ套接字事件监听与IO多路复用收发消息

相关链接:

示例代码:

使用:zeromq/libzmqlibzmq 4.3.3

API接口: http://api.zeromq.org

GitHub:SocketMonitor

服务端:

#include <iostream>
#include <zmq.h>
#include <thread>
#include <cstring>

class ZeroMQMonitorTest{

public:
    ZeroMQMonitorTest(){
        bOnRunning = false;
        /// 1、创建上下文
        context = zmq_ctx_new();
        /// 2、创建 socket
        socket = zmq_socket(context,ZMQ_DEALER);
        /// 3、如有需要设置 套接字选项
        int ivl = 0;
        // ZMQ_HANDSHAKE_IVL 设定最大握手间隔 这里0不超时失败,
        // 不同系统间通过zeromq通讯时,内部握手机制 在加密协议下会握手失败
        zmq_setsockopt(socket,ZMQ_HANDSHAKE_IVL,&ivl,sizeof(int));

    }
    ~ZeroMQMonitorTest() {
        bOnRunning = false;
        /// 关闭套接字、销毁上下文
        zmq_close (socket);
        zmq_ctx_destroy (context);
    }
    int start(){
        bOnRunning = true;
        /// 4、绑定 socket
        zmq_bind(socket, "tcp://0.0.0.0:5555");

        /// 创建线程 监听 套接字事件  ZMQ_EVENT_ALL 所有事件
        zmq_socket_monitor(socket, "inproc://monitor.inproc", ZMQ_EVENT_ALL);
        /// 监听线程
        std::thread monitorThread(&ZeroMQMonitorTest::socketMonitor,this);
        pthread_setname_np(monitorThread.native_handle(), "monitorThread");
        monitorThread.detach();

        /// 数据线程  用于 socket 数据通讯
        std::thread pollerThread(&ZeroMQMonitorTest::socketPoller,this);
        pthread_setname_np(pollerThread.native_handle(), "pollerThread");
        pollerThread.detach();
        return 0;
    }
    int stop() {
        bOnRunning = false;
        return 0;
    }

private:
    void *context; // ZeroMQ上下文
    void *socket; // ZeroMQ socket
    bool bOnRunning;

    void socketMonitor() {
        uint16_t event;  // 事件 number (16 bits)
        int32_t value;  // 事件 value (32 bits)
        std::cout << "starting monitor..." << std::endl;

        /// 为了收集 zmq_socket_monitor 监听的套接字事件
        /// 1、创建 ZMQ_PAIR 事件监听 套接字 2、连接到 zmq_socket_monitor 中的 监听地址  "inproc://monitor.inproc"
        /// 参看 http://api.zeromq.org/master:zmq-socket-monitor
        void *monitor = zmq_socket(context, ZMQ_PAIR);
        zmq_connect(monitor, "inproc://monitor.inproc");

        /* zmq_pollitem_t 结构体 用于 zmq_poll
        typedef struct
        {
            void //*socket//;
            int //fd//;
            short //events//;
            short //revents//;
        } zmq_pollitem_t;
        socket fd 同时设置 套接字优先
        revents 被先清除,后有事件来后被置位
        */
        zmq_pollitem_t items[2];
        items[0].socket = monitor;
        items[0].events = ZMQ_POLLIN ;
        /// ZMQ_POLLIN : 从套接字接收至少一个消息而不阻塞
        /// ZMQ_POLLOUT : 至少可以向套接字发送一条消息而不阻塞。

        zmq_msg_t msg; // 消息结构体

        while(bOnRunning) {
            /// IO多路复用  1 表示使用 items 数组中的第一个
            /// timeout值是 -1,zmq_poll()进入无限阻塞等待状态,直到至少一个zmq_pollitem_t项上请求的事件发生了
            zmq_poll(items,1, 1000);

            if (!(items[0].revents & ZMQ_POLLIN)) {
                // 监听的事件为 ZMQ_POLLIN 应该进不来
                continue;
            }
            /// 接收消息帧
            /**
             * Each event is sent as two frames.
             * The first frame contains an event number (16 bits), and an event value (32 bits) that provides additional data according to the event number.
             * The second frame contains a string that specifies the affected endpoint.
             */
            /// 每个事件发送两帧数
            /// The first frame
            zmq_msg_init(&msg); /// 初始化msg引用的Message对象以表示空消息。
            zmq_msg_recv(&msg, monitor, 0); /// 从套接字接收消息部件

            /// zmq_msg_data 返回指向msg引用的消息对象的消息内容的指针。
            /// 永远不要直接访问zmq_msg_t成员,而是始终使用zmq_msg系列函数。
            const char *data = (const char *) zmq_msg_data(&msg);
            memcpy(&event, data, 2);  /// event number  16bits
            memcpy(&value, data + 2, 4);  /// event value  32bits
            zmq_msg_close(&msg); // 释放ZeroMQ消息

            /// The second frame
            zmq_msg_init(&msg);
            zmq_msg_recv(&msg, monitor, 0);

            int size = zmq_msg_size(&msg);
            data = (const char *)zmq_msg_data(&msg);
            char *address = new char[size + 1];
            memcpy(address, data, size);

            zmq_msg_close(&msg);

            switch (event){
                case ZMQ_EVENT_LISTENING:
                    std::cout << "listening socket address " << address << std::endl;
                    break;
                case ZMQ_EVENT_ACCEPTED:
                    std::cout << "accepted socket address " << address << std::endl;
                    break;
                case ZMQ_EVENT_CONNECTED:
                    std::cout << "connected socket address " << address << std::endl;
                    break;
                case ZMQ_EVENT_CLOSE_FAILED:
                    std::cout << "socket address " << address << std::endl;
                    break;
                case ZMQ_EVENT_CLOSED:
                    std::cout << "closed socket address " << address << std::endl;
                    break;
                case ZMQ_EVENT_DISCONNECTED:
                    std::cout << "disconnected socket address " << address << std::endl;
                    break;
                case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL:
                    std::cout << "HANDSHAKE_FAILED " << address << std::endl;
                default:
                    break;
            }
            delete[] address;
            std::cout << "socket event value " << value << std::endl;
            std::cout << "socket event num " << event << std::endl;
        }
        zmq_close(monitor);
    }

    void socketPoller() {
        zmq_pollitem_t item;
        item.socket = socket;
        item.events = ZMQ_POLLIN;
        zmq_msg_t msg;
        while (bOnRunning) {
            zmq_poll(&item, 1, 1000);
            if(item.revents == ZMQ_POLLIN) {
                zmq_msg_init(&msg);
                zmq_msg_recv(&msg, socket, 0);

                /// zmq_msg_size 以字节为单位,检索消息内容大小
                /// 字符串并未包含结束符
                int size = zmq_msg_size(&msg);
                std::cout << "msg size = " << zmq_msg_size(&msg) << std::endl;
                const char *data = (const char *)zmq_msg_data(&msg);
                char *str = new char[size+1]{'\0'}; //初始化为 '\0'   zmq_msg_recv 接收不带结束符
                memcpy(str, data, size);
                std::cout << "Receive: \n" << str << std::endl;
                zmq_msg_close(&msg);

                //回传
                /// 初始化指定大小的zeromq消息
                zmq_msg_init_size(&msg, strlen(str));
                memcpy(zmq_msg_data(&msg), data, strlen(str));
                zmq_msg_send(&msg, socket, 0);
                delete[] str;
            }
        }
        int test = 1;
    }
};


int main() {
    std::cout << "Hello, World!" << std::endl;
    ZeroMQMonitorTest zeroMqMonitorTest;
    zeroMqMonitorTest.start();
    getchar();
    zeroMqMonitorTest.stop();
    return 0;
}

客户端:


//  Hello World client
#include <zmq.h>
#include <cstdio>
#include <iostream>
#include <cstring>

int main ()
{
    /// 创建上下文
    void *context = zmq_ctx_new();
    /// 创建ZeroMQ socket
    void *socket = zmq_socket(context,ZMQ_DEALER);
    int ivl = 0;
    zmq_setsockopt(socket,ZMQ_HANDSHAKE_IVL,&ivl,sizeof(int));
    std::cout << "输入连接的ip: " << std::endl;
    char *line = nullptr;
    size_t len = 0;
    ssize_t num;
    num = getline(&line, &len, stdin);
    std::string ip = line;
    ip[num-1] = ':'; //去掉获取字符串最后的 \n
    //TODO 健康检测,校验ip地址输入
    std::string tcpconnect = "tcp://" + ip + "5555";
    /// 连接
    zmq_connect (socket, tcpconnect.c_str());
    free(line);


    ///循环发送数据、接收数据  使用 zmq_msg_send zmq_msg_recv zmq_poll
    zmq_msg_t msg;
    zmq_pollitem_t item;
    item.socket = socket;
    item.events = ZMQ_POLLOUT;
    while (true) {
        zmq_poll(&item, 1, 1000);
        if(item.revents & ZMQ_POLLOUT) {
            std::cout << "Send:"  << std::endl;
            char * input = nullptr;;
            num = getline(&input, &len, stdin);
            /// 4.发送数据
            /// 初始化指定大小的zeromq消息
            zmq_msg_init_size(&msg, num-1);
            memcpy(zmq_msg_data(&msg), line, num-1);
            int ret = zmq_msg_send(&msg, socket, 0);
            free(input);
            input = nullptr;
            zmq_msg_close(&msg);

            // 5.接收回复数据
            zmq_msg_init(&msg);
            zmq_msg_recv(&msg, socket, 0);

            int size = zmq_msg_size(&msg);
            std::cout << "msg size = " << zmq_msg_size(&msg) << std::endl;
            const char *data = (const char *)zmq_msg_data(&msg);
            char *str = new char[size+1];
            memcpy(str, data, size);
            std::cout << "Receive: \n" << str << std::endl;
            zmq_msg_close(&msg);
        }



    }
//    ///循环发送数据、接收数据  使用 zmq_msg_send zmq_msg_recv
//    while (true) {
//        std::cout << "Send:"  << std::endl;
//        num = getline(&line, &len, stdin);
//        /// 4.发送数据
//        /// 初始化指定大小的zeromq消息
//        zmq_msg_init_size(&msg, num);
//        memcpy(zmq_msg_data(&msg), line, num-1);
//        int ret = zmq_msg_send(&msg, socket, 0);
//        free(line);
//        zmq_msg_close(&msg);
//        // 5.接收回复数据
//        zmq_msg_init(&msg);
//        zmq_msg_recv(&msg, socket, 0);
//
//        int size = zmq_msg_size(&msg);
//        std::cout << "msg size = " << zmq_msg_size(&msg) << std::endl;
//        const char *data = (const char *)zmq_msg_data(&msg);
//        char *str = new char[size+1];
//        memcpy(str, data, size);
//        std::cout << "Receive: \n" << str << std::endl;
//        zmq_msg_close(&msg);
//    }

//    ///循环发送数据、接收数据  使用 zmq_send zmq_recv
//    while(true) {
//        char buffer [255];
//        std::cout << "Send:"  << std::endl;
//        num = getline(&line, &len, stdin); // 包括\n
//        zmq_send (socket, line, num - 1, 0);
//        free(line);
//        // 5.接收回复数据
//        zmq_recv (socket, buffer, 255, 0);
//        std::cout << "Receive: \n" << buffer << std::endl;
//    }

    /// 关闭套接字、销毁上下文
    zmq_close (socket);
    zmq_ctx_destroy (context);

    return 0;

}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值