上面那讲比较简单,感觉也学不出个所以然来,所以这一节开始总结归纳,为什么不是上一节开始总结归纳呢,因为上一节课学习的时候,我也不是很懂,不过,这节课之后,我渐渐有点感觉了,接下来,就是我遍学习遍做的思路。
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模型
上一节说的发布订阅模型,其实有两个问题:
- 如果服务器先启用,客户端后启用,客户端就不能接受到前面服务器发的消息。
- 如果我要扩展一个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套接字,大家可以去看看,英语好的也可以直接看英文。