后台开发学习笔记(二十三、libzmq模型 上)

上面那讲比较简单,感觉也学不出个所以然来,所以这一节开始总结归纳,为什么不是上一节开始总结归纳呢,因为上一节课学习的时候,我也不是很懂,不过,这节课之后,我渐渐有点感觉了,接下来,就是我遍学习遍做的思路。

23.1 分析上一节课的代码

23.1.1 context_t类

这是zmq的一个类,分析c++代码,我们先看一下类的构造函数和析构函数

class context_t
{
  public:
    context_t()
    {
        ptr = zmq_ctx_new();
        if (ptr == ZMQ_NULLPTR)
            throw error_t();
    }


    explicit context_t(int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT)
    {
        ptr = zmq_ctx_new();
        if (ptr == ZMQ_NULLPTR)
            throw error_t();

        int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_);
        ZMQ_ASSERT(rc == 0);

        rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_);
        ZMQ_ASSERT(rc == 0);
    }

#ifdef ZMQ_HAS_RVALUE_REFS
    context_t(context_t &&rhs) ZMQ_NOTHROW : ptr(rhs.ptr) { rhs.ptr = ZMQ_NULLPTR; }
    context_t &operator=(context_t &&rhs) ZMQ_NOTHROW
    {
        close();
        std::swap(ptr, rhs.ptr);
        return *this;
    }
#endif

    ~context_t() ZMQ_NOTHROW { close(); }
	operator void *() ZMQ_NOTHROW { return _handle; }

    operator void const *() const ZMQ_NOTHROW { return _handle; }

    void close() ZMQ_NOTHROW
    {
        if (_handle == ZMQ_NULLPTR)
            // already closed
            return;
        int rc = zmq_close(_handle);
        ZMQ_ASSERT(rc == 0);
        _handle = ZMQ_NULLPTR;
    }
}

我们上一节代码用的是,传入一个变量,所以一个是调用了第二个构造函数

23.1.2 context_t()
explicit context_t(int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT)
{
     ptr = zmq_ctx_new();
     if (ptr == ZMQ_NULLPTR)
         throw error_t();

     int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_);
     ZMQ_ASSERT(rc == 0);

     rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_);
     ZMQ_ASSERT(rc == 0);
 }

这个构造函数就是从类中复制出来的,这个函数首先调用了
zmq_ctx_new:创建一个context
zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_):创建最大的线程数,如果是服务器的话,就可以填多一点
zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_):最大的socket数

23.1.3 socket_t 类

这个也是zmq的一个类,

class socket_t : public detail::socket_base
{
    friend class monitor_t;

  public:
    socket_t() ZMQ_NOTHROW : detail::socket_base(ZMQ_NULLPTR), ctxptr(ZMQ_NULLPTR) {}

    socket_t(context_t &context_, int type_) :
        detail::socket_base(zmq_socket(static_cast<void *>(context_), type_)),
        ctxptr(static_cast<void *>(context_))
    {
        if (_handle == ZMQ_NULLPTR)
            throw error_t();
    }

#ifdef ZMQ_CPP11
    socket_t(context_t &context_, socket_type type_) :
        socket_t(context_, static_cast<int>(type_))
    {
    }
#endif

#ifdef ZMQ_HAS_RVALUE_REFS
    socket_t(socket_t &&rhs) ZMQ_NOTHROW : detail::socket_base(rhs._handle),
                                           ctxptr(rhs.ctxptr)
    {
        rhs._handle = ZMQ_NULLPTR;
        rhs.ctxptr = ZMQ_NULLPTR;
    }
    socket_t &operator=(socket_t &&rhs) ZMQ_NOTHROW
    {
        close();
        std::swap(_handle, rhs._handle);
        return *this;
    }
#endif

    ~socket_t() ZMQ_NOTHROW { close(); }

    operator void *() ZMQ_NOTHROW { return _handle; }

    operator void const *() const ZMQ_NOTHROW { return _handle; }

    void close() ZMQ_NOTHROW
    {
        if (_handle == ZMQ_NULLPTR)
            // already closed
            return;
        int rc = zmq_close(_handle);
        ZMQ_ASSERT(rc == 0);
        _handle = ZMQ_NULLPTR;
    }
}

实例化这个类的时候,调用了这个zmq创建socket函数,今天主要是讲这个
zmq_socket(static_cast<void *>(context_), type_)
第一个参数就是我们上面创建的context对象,第二个参数就是zmq自己封装的socket类型,接下来我们会按模式分类讲解一波。

23.2 zmq通信模型

23.2.1 Client-server模型

服务器和客户端通信模式,反正也不是很熟,先写在这里,如果有用到的话,可以补充。

ZMQ_CLIENT

ZMQ_SERVER

这个功能是4.2.0版本以上才支持,稳定版本默认不开启,以后在使用。

23.2.2 Radio-dish模型

新的模式还真的有点不熟悉,以后熟悉了再看看怎么使用把。

ZMQ_RADIO

ZMQ_DISH

23.2.3 Publish-subscribe模型

发布订阅模式,这个模式可以玩玩。
在这里插入图片描述
PUB是发布者,SUB是订阅者

客户端程序:

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


using namespace std;
// export LD_LIBRARY_PATH="&LD_LIBRARY_PATH:/usr/local/lib"
// g++ main.cpp -lzmq -o main
/**
    * @brief  Main构造函数
    * @param  
    * @retval 
    */ 
int main(int argc, char *argv)
{
    zmq::context_t context (1);
    #if 0
    zmq::socket_t socket(context, ZMQ_CLIENT);
    char buff[1024] = "Hello, world";

    socket.connect("tcp://122.51.111.216:5555");

    zmq::message_t mess(buff, strlen(buff));
    socket.send(mess);
    //socket.send(buff, strlen(buff));
    //socket.recv(buff, 1024);
    #endif

     zmq::socket_t socket(context, ZMQ_SUB);
     socket.connect("tcp://122.51.111.216:5555");

     socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);    //全部消息都收到
     //socket.setsockopt(ZMQ_SUBSCRIBE, "order ", strlen("order "));   //只收到order消息

     for(int i=0; i<100; i++) {
         zmq::message_t mess;
        socket.recv(&mess);

        printf("接收到的数据 %s\n", (char *)zmq_msg_data(mess.handle()));
     }

    socket.setsockopt(ZMQ_UNSUBSCRIBE, "order ", strlen("order "));    //取消订阅
    for(int i=0; i<100; i++) {
         zmq::message_t mess;
        socket.recv(&mess);

        printf("取消后接收到的数据 %s\n", (char *)zmq_msg_data(mess.handle()));
     }

    socket.close();
    context.close();

    return 0;
}

服务器程序:

#include <iostream>
#include <string.h>
#include "zmq.hpp"
#include "stdio.h"
#include <time.h>
#include <unistd.h>


// export LD_LIBRARY_PATH="&LD_LIBRARY_PATH:/usr/local/lib"
using namespace std;

char* topics[] = {
        "order",
        "realplay",
        "advertising",
        "search",
        "authority",
        "log"
};

char* msgs[] = {
        "100 dozens of durex",
        "Kim Kardashianw underwear promotion live",
        "Invisible extreme thin",
        "Aircraft Cup",
        "lee login",
        "20140217T104528.495629|fnen|k3r=testcases/ClockSelection.ttcn3:1426|components.f_copy_logs(testcase_name="
};


/**
    * @brief  Main构造函数
    * @param  
    * @retval 
    */ 
int main(int argc, char *argv)
{
    zmq::context_t context (1);

    #if 0
    zmq::socket_t socket(context, ZMQ_SERVER);
    char buff[1024] = {0};

    socket.bind("tcp://*:5555");

    // while(1) {
        //socket.recv(buff, 1024);
        //printf("buff %s\n", buff);
    // }
    void *data = NULL;
    zmq::message_t mess;
    socket.recv(&mess);

    data = zmq_msg_data(mess.handle());
    printf("接收到的数据 %s\n", (char *)data);
    #endif
    zmq::socket_t socket(context, ZMQ_PUB);

    socket.bind("tcp://*:5555");
    srandom ((unsigned) time (NULL));
    int count = 0;
    
    while(1) {
        int index = random() % (sizeof(topics) / sizeof(topics[0]));

        zmq::message_t mess(128);
        snprintf ((char *) mess.data(), 128 , "%s %s , count = %d", topics[index], msgs[index], count);
        std::cout << "send message is : " << (char*)mess.data() << std::endl;
        socket.send(mess);
        count ++;
        sleep(1);

    }


    socket.close();
    context.close();

    return 0;
}

总结:
1.通过socket选项ZMQ_SUBSCRIBE去订阅指定的主题,比如order log。(order后面需要加一个空格)
2.如果想订阅所有的主题,则可以设置ZMQ_SUBSCRIBE为“”。
3.通过ZMQ_UNSUBSCRIBE去掉某主题的订阅
4.如果不订阅主题,则收不到任何消息。

23.2.4 xPub-xsub模型

上一节说的发布订阅模型,其实有两个问题:

  1. 如果服务器先启用,客户端后启用,客户端就不能接受到前面服务器发的消息。
  2. 如果我要扩展一个pub的模型,以前的客户端的sub全部不兼容,水平扩展性不好。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
还想添加一个服务器发送程序,因为端口占用了,不能添加。

所以基于上述两个问题,libzmq升级了一下套接字,就是我们现在要说的xSUB和xPUB模型
在这里插入图片描述
在PUB和SUB中间加了一个代理层(Proxy),统一由这个Proxy来转发这些消息,具体实现我也不知道,现在我们先用,以后有空可以分析一下

客户端代码:

/**
    ******************************************************************************
    * @file    main.cpp
    * @author  debian
    * @version V1.0.0
    * @date    2020-03-22
    * @brief   zmq测试程序
    ******************************************************************************
    * @attention
    *
    *
    ******************************************************************************
    */ 

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/** @addtogroup DataStruct_Driver
    * @{
    */

/** @addtogroup main
    * @{
    */

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/


/** @defgroup main_Exported_Functions main Exported Functions
    * @{
    */

/** @defgroup main_Exported_Functions_Group1 Initialization and deinitialization functions
    *  @brief    Initialization and Configuration functions
    *
@verbatim    
    ===============================================================================
                ##### Initialization and deinitialization functions #####
    ===============================================================================
    [..]
        This section provides functions allowing to initialize and de-initialize the main
        to be ready for use.
 
@endverbatim
    * @{
    */ 
#include <iostream>
#include <string.h>
#include "zmq.hpp"


using namespace std;
// export LD_LIBRARY_PATH="&LD_LIBRARY_PATH:/usr/local/lib"
// g++ main.cpp -lzmq -o main
/**
    * @brief  Main构造函数
    * @param  
    * @retval 
    */ 
int main(int argc, char *argv)
{
    zmq::context_t context (1);
    #if 0
    zmq::socket_t socket(context, ZMQ_CLIENT);
    char buff[1024] = "Hello, world";

    socket.connect("tcp://122.51.111.216:5555");

    zmq::message_t mess(buff, strlen(buff));
    socket.send(mess);
    //socket.send(buff, strlen(buff));
    //socket.recv(buff, 1024);
    #endif

     zmq::socket_t socket(context, ZMQ_SUB);
     socket.connect("tcp://122.51.111.216:5556");

     socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);    //全部消息都收到
     //socket.setsockopt(ZMQ_SUBSCRIBE, "order ", strlen("order "));   //只收到order消息

     for(int i=0; i<10; i++) {
         zmq::message_t mess;
        socket.recv(&mess);

        printf("接收到的数据 %s\n", (char *)zmq_msg_data(mess.handle()));
     }
  
    socket.setsockopt(ZMQ_UNSUBSCRIBE, "order ", strlen("order "));     //取消订阅
    for(int i=0; i<100; i++) {
         zmq::message_t mess;
        socket.recv(&mess);

        printf("取消后接收到的数据 %s\n", (char *)zmq_msg_data(mess.handle()));
     }

    socket.close();
    context.close();

    return 0;
}

/**
    * @brief  Main析构函数
    * @param  
    * @retval 
    */ 
//g++ main.cpp -lzmq -Wl,-rpath=/usr/local/lib -o main
    
/**
    * @}
    */

/** @defgroup main_Exported_Functions_Group2 operation functions 
    *  @brief   operation functions
    *
@verbatim   
    ===============================================================================
                        ##### operation functions #####
    ===============================================================================
    [..]
        This subsection provides a set of functions allowing to manage the main.

@endverbatim
    * @{
    */

        /* 操作函数写在这里 */

    /**
    * @}
    */


/**
    * @}
    */

/**
    * @}
    */

/**
    * @}
    */

/************************ (C) 2020 HOME 深圳龙华 *****END OF FILE****/

服务器端代码:

/**
    ******************************************************************************
    * @file    main.cpp
    * @author  debian
    * @version V1.0.0
    * @date    2020-03-22
    * @brief   zmq测试程序
    ******************************************************************************
    * @attention
    *
    *
    ******************************************************************************
    */ 

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/** @addtogroup DataStruct_Driver
    * @{
    */

/** @addtogroup main
    * @{
    */

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/


/** @defgroup main_Exported_Functions main Exported Functions
    * @{
    */

/** @defgroup main_Exported_Functions_Group1 Initialization and deinitialization functions
    *  @brief    Initialization and Configuration functions
    *
@verbatim    
    ===============================================================================
                ##### Initialization and deinitialization functions #####
    ===============================================================================
    [..]
        This section provides functions allowing to initialize and de-initialize the main
        to be ready for use.
 
@endverbatim
    * @{
    */ 
#include <iostream>
#include <string.h>
#include "zmq.hpp"
#include "stdio.h"
#include <time.h>
#include <unistd.h>


// export LD_LIBRARY_PATH="&LD_LIBRARY_PATH:/usr/local/lib"
using namespace std;

char* topics[] = {
        "order",
        "realplay",
        "advertising",
        "search",
        "authority",
        "log"
};

char* msgs[] = {
        "100 dozens of durex",
        "Kim Kardashianw underwear promotion live",
        "Invisible extreme thin",
        "Aircraft Cup",
        "lee login",
        "20140217T104528.495629|fnen|k3r=testcases/ClockSelection.ttcn3:1426|components.f_copy_logs(testcase_name="
};


/**
    * @brief  Main构造函数
    * @param  
    * @retval 
    */ 
int main(int argc, char *argv)
{
    zmq::context_t context (1);

    #if 0
    zmq::socket_t socket(context, ZMQ_SERVER);
    char buff[1024] = {0};

    //socket.bind("tcp://*:5555");
    socket.connect("tcp://0.0.0.0:5555");

    // while(1) {
        //socket.recv(buff, 1024);
        //printf("buff %s\n", buff);
    // }
    void *data = NULL;
    zmq::message_t mess;
    socket.recv(&mess);

    data = zmq_msg_data(mess.handle());
    printf("接收到的数据 %s\n", (char *)data);
    #endif
    zmq::socket_t socket(context, ZMQ_PUB);

    //socket.bind("tcp://*:5555");
    socket.connect("tcp://122.51.111.216:5555");
    srandom ((unsigned) time (NULL));
    int count = 0;
    
    while(1) {
        int index = random() % (sizeof(topics) / sizeof(topics[0]));

        zmq::message_t mess(128);
        snprintf ((char *) mess.data(), 128 , "%s %s , count = %d", topics[index], msgs[index], count);
        std::cout << "send message is : " << (char*)mess.data() << std::endl;
        socket.send(mess);
        count ++;
        sleep(1);

    }


    socket.close();
    context.close();

    return 0;
}



/**
    * @brief  Main析构函数
    * @param  
    * @retval 
    */ 

    
/**
    * @}
    */

/** @defgroup main_Exported_Functions_Group2 operation functions 
    *  @brief   operation functions
    *
@verbatim   
    ===============================================================================
                        ##### operation functions #####
    ===============================================================================
    [..]
        This subsection provides a set of functions allowing to manage the main.

@endverbatim
    * @{
    */

        /* 操作函数写在这里 */

    /**
    * @}
    */


/**
    * @}
    */

/**
    * @}
    */

/**
    * @}
    */

/************************ (C) 2020 HOME 深圳龙华 *****END OF FILE****/

proxy代码:

/**
    ******************************************************************************
    * @file    proxy.cpp
    * @author  debian
    * @version V1.0.0
    * @date    2020-03-28
    * @brief   zmq代理
    ******************************************************************************
    * @attention
    *
    *
    ******************************************************************************
    */ 

/* Includes ------------------------------------------------------------------*/


/** @addtogroup DataStruct_Driver
    * @{
    */

/** @addtogroup proxy
    * @{
    */

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/


/** @defgroup proxy_Exported_Functions proxy Exported Functions
    * @{
    */

/** @defgroup proxy_Exported_Functions_Group1 Initialization and deinitialization functions
    *  @brief    Initialization and Configuration functions
    *
@verbatim    
    ===============================================================================
                ##### Initialization and deinitialization functions #####
    ===============================================================================
    [..]
        This section provides functions allowing to initialize and de-initialize the proxy
        to be ready for use.
 
@endverbatim
    * @{
    */ 
#include <iostream>
#include <string.h>
#include "zmq.hpp"
#include <unistd.h>
#include <stddef.h>
//g++ proxy.cpp -std=c++11 -lzmq -o proxy
/**
    * @brief  Proxy构造函数
    * @param  
    * @retval 
    */ 
    int main(int argc, char *argv)
    {
        zmq::context_t context (1);

        zmq::socket_t xpub(context, ZMQ_XPUB);
        //socket.connect("tcp://122.51.111.216:5555");
        xpub.bind("tcp://*:5556");
        std::cout << "bind" << std::endl;
        zmq::socket_t xsub(context, ZMQ_XSUB);
        //socket.connect("tcp://122.51.111.216:5555");
        xsub.bind("tcp://*:5555");
        std::cout << "bind3" << std::endl;
        sleep(1);
        std::cout << "bind4" << std::endl;
        //zmq::proxy(&xsub, &xpub, nullptr);    //这个是错误的
        zmq::proxy((void*)xpub, (void*)xsub, nullptr);
        std::cout << "start proxy" << std::endl;


        return 0;
    }

/**
    * @brief  Proxy析构函数
    * @param  
    * @retval 
    */ 

    
/**
    * @}
    */

/** @defgroup proxy_Exported_Functions_Group2 operation functions 
    *  @brief   operation functions
    *
@verbatim   
    ===============================================================================
                        ##### operation functions #####
    ===============================================================================
    [..]
        This subsection provides a set of functions allowing to manage the proxy.

@endverbatim
    * @{
    */

        /* 操作函数写在这里 */

    /**
    * @}
    */


/**
    * @}
    */

/**
    * @}
    */

/**
    * @}
    */

/************************ (C) 2020 HOME 深圳龙华 *****END OF FILE****/

这三端代码,需要注意的是服务器端,从以前的bind变成了connect,就是变成连接proxy了,然后proxy会bing两个端口,然后调用proxy函数就行代理转发。(取消订阅这个没看懂是怎么用的,说要把一个字节变为0,官网都没例子,靠猜还是有点累)。

在这里插入图片描述

还有一个问题,就是服务器先启动的时候,客户端后执行,这时候客户端是接收不到前面的数据的,所以这时候**就可能用到proxy的第三个参数了:void proxy(void *frontend, void backend, void capture)
capture就是获取到的前面的数据的缓冲区。

好像具体怎么用,我也不知道,以后再补补吧,以后去过有这种需求再继续研究研究把。

其实这个代理实现的也并不好,没有像其他的方式实现,具体我也不知道,以后知道了再分析分析。

还有一个比较好的翻译文章:ZeroMQ接口函数之 :zmq_socket – 创建ZMQ套接字,大家可以去看看,英语好的也可以直接看英文。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值