ZeroMQ:18---模式之(发布-订阅封包)

一、发布-订阅封包

  • 在发布-订阅模式中,我们可以将键分割成我们称之为封包的一个单独的消息帧。如果你想使用发布-订阅封包,可以自己建立它们。它是可选的,并且在以前的发布-订阅的例子中,我们并没有这么做。在简单的情况下,使用发布-订阅封包是多了一点工作,但它更清晰,尤其是对真实案例来说,其中键和数据本质上是不同的东西
  • 下图显示的是一种使用封包的发布-订阅消息的样子

ZeroMQ:18---模式之(发布-订阅封包)_第1张图片

  • 订阅执行的是前缀匹配。也就是说,订阅者会寻找:“以XXX开头的所有消息”。现在的问题是:如何将数据与键分隔开,以免前缀匹配不小心匹配了数据。最好的答案是使用一个封包,因为这个匹配将不会跨越帧边界

二、代码实现

发布者代码如下

  • 本次使用的发送者会发送两种类型的消息,A和B
  • 其中我们将一个发布-订阅消息拆分为两部分进行发送,第一部分为消息的类型,第二部分为消息的实际信息,第一部分调用s_sendmore()发送,指定发送的是多部分消息,第二部分使用s_send()发送,发送的是非多部分消息
  • 因此,我们设计的发布-订阅的一个完整的包为包头+包体
// psenvpub.c
// 源码链接: https://github.com/dongyusheng/csdn-code/blob/master/ZeroMQ/psenvpub.c
#include 
#include 
#include 
#include 
#include 
#include 

// 向socket套接字发送string信息
static int s_send(void *socket, char *string);
// 向socket发送消息, 发送时zmq_msg_send()函数使用ZMQ_SNDMORE套接字选项
static int s_sendmore(void *socket, char *string);


int main()
{
    int rc;
    // 1.初始化上下文
    void *context = zmq_ctx_new();

    // 2.创建、绑定套接字
    void *publisher = zmq_socket(context, ZMQ_PUB);
    assert(publisher != NULL);
    
    rc = zmq_bind(publisher, "tcp://*:5563");
    assert(rc != -1);

    // 3.循环发送消息
    while(1)
    {
        // 4.发送A类型的消息
        rc = s_sendmore(publisher, "A");                      //包头
        assert(rc != -1);
        rc = s_send(publisher, "We dont't want to see this"); //包体
        assert(rc != -1);

        // 5.发送B类型的消息
        rc = s_sendmore(publisher, "B");                      //包头
        assert(rc != -1);
        rc = s_send(publisher, "We would like to see this"); //包体
        assert(rc != -1);

        sleep(1);
    }

    // 6.关闭套接字、清除上下文
    zmq_close(publisher);
    zmq_ctx_destroy(context);

    return 0;
}


static int s_send(void *socket, char *string)
{
    int rc;
    
    zmq_msg_t msg;
    zmq_msg_init_size(&msg, strlen(string));
    memcpy(zmq_msg_data(&msg), string, strlen(string));
    
    rc = zmq_msg_send(&msg, socket, 0);
    
    zmq_msg_close(&msg);
    
    return rc;
}

static int s_sendmore(void *socket, char *string)
{
    int rc;
    
    zmq_msg_t msg;
    zmq_msg_init_size(&msg, strlen(string));
    memcpy(zmq_msg_data(&msg), string, strlen(string));
    
    rc = zmq_msg_send(&msg, socket, ZMQ_SNDMORE);
    
    zmq_msg_close(&msg);
    
    return rc;
}

订阅者代码如下

  • 其只接收类型为B的消息,因为发布者将消息-订阅的消息进行了封包,因此我们需要连续调用两次s_recv()才能将一个完整的信息进行接收
  • 因为多部分消息是作为一个完整的消息体在链路中传输的,因此订阅者不需要考虑消息实体如果没有订阅关键字是否会接收到
// psenvsub.c
// 源码链接: https://github.com/dongyusheng/csdn-code/blob/master/ZeroMQ/psenvsub.c
#include 
#include 
#include 
#include 
#include 

// 向socket套接字发送string信息
static char *s_recv(void *socket);

int main()
{
    int rc;
    // 1.初始化上下文
    void *context = zmq_ctx_new();

    // 2.创建、绑定套接字
    void *subscriber = zmq_socket(context, ZMQ_SUB);
    assert(subscriber != NULL);
    rc = zmq_connect(subscriber, "tcp://localhost:5563");
    assert(rc != -1);

    // 3.设置套接字选项, 设置自己订阅带有"B"的相关订阅信息
    rc = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "B", 1);
    assert(rc != -1);

    
    // 4.循环接收消息
    while(1)
    {
        // 5.读取包头信息
        char *address = s_recv(subscriber);
        assert(address != NULL);

        // 判断是否是多部分消息, 这一步可以省略, 因为我们通过源码可以看到订阅者就是发送的多部分消息
        //int more;
        //size_t more_size = sizeof(more);
        //zmq_getsockopt(subscriber, ZMQ_RCVMORE, &more, &more_size);

        // 6.读取包体信息
        char *contents = s_recv(subscriber);
        assert(contents != NULL);
        printf("%s: %s\n", address, contents);

        free(address);
        free(contents);
    }

    // 7.关闭套接字、清除上下文
    zmq_close(subscriber);
    zmq_ctx_destroy(context);

    return 0;
}


static char *s_recv(void *socket)
{
    int rc;
    zmq_msg_t msg;
    zmq_msg_init(&msg);
    
    rc = zmq_msg_recv(&msg, socket, 0);
    if(rc == -1)
        return NULL;

    char *string = (char*)malloc(rc + 1);
    memcpy(string, zmq_msg_data(&msg), rc);
    
    zmq_msg_close(&msg);

    string[rc] = 0;
    
    return string;
}
  • 编译运行如下,左侧为订阅者,右侧为发布者,订阅者只接收到“B”类型的消息
gcc -o psenvpub psenvpub.c -lzmq
gcc -o psenvsub psenvsub.c -lzmq

ZeroMQ:18---模式之(发布-订阅封包)_第2张图片

三、扩展

  • 如果你订阅了多个发布者,而你想知道它们的地址,以便可以通过另一个套接字发送数据给它们,可以创建一个由三部分组成的消息,如下图所示:

ZeroMQ:18---模式之(发布-订阅封包)_第3张图片

  • 当然,封包的格式可以由你自由设计

你可能感兴趣的:(ZeroMQ,发布-订阅封包)