使用inproc套接字的ZeroMQ PubSub永远挂起

时间:2019-09-14 07:14:57

标签: c++ multithreading zeromq communication

我正在改编tcp PubSub示例以将inproc与多线程一起使用。它最终永远挂着。

我的设置

  • macOS Mojave,Xcode 10.3
  • zmq 4.3.2

重新产生问题的源代码:

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <thread>
#include "zmq.h"

void hello_pubsub_inproc() {
    void* context = zmq_ctx_new();
    void* publisher = zmq_socket(context, ZMQ_PUB);
    printf("Starting server...\n");
    int pub_conn = zmq_bind(publisher, "inproc://*:4040");

    void* subscriber = zmq_socket(context, ZMQ_SUB);
    printf("Collecting stock information from the server.\n");
    int sub_conn = zmq_connect(subscriber, "inproc://localhost:4040");
    sub_conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, 0, 0);

    std::thread t_pub = std::thread([&]{
        const char* companies[2] = {"Company1", "Company2"};
        int count = 0;
        for(;;) {
            int which_company = count % 2;
            int index = (int)strlen(companies[0]);
            char update[12];
            snprintf(update, sizeof update, "%s",
                     companies[which_company]);
            zmq_msg_t message;
            zmq_msg_init_size(&message, index);
            memcpy(zmq_msg_data(&message), update, index);
            zmq_msg_send(&message, publisher, 0);
            zmq_msg_close(&message);
            count++;
        }
    });

    std::thread t_sub = std::thread([&]{
        int i;
        for(i = 0; i < 10; i++) {
            zmq_msg_t reply;
            zmq_msg_init(&reply);
            zmq_msg_recv(&reply, subscriber, 0);
            int length = (int)zmq_msg_size(&reply);
            char* value = (char*)malloc(length);
            memcpy(value, zmq_msg_data(&reply), length);
            zmq_msg_close(&reply);
            printf("%s\n", value);
            free(value);
        }
    });

    t_pub.join();

    // Give publisher time to set up.
    sleep(1);

    t_sub.join();

    zmq_close(subscriber);
    zmq_close(publisher);
    zmq_ctx_destroy(context);
}

int main (int argc, char const *argv[]) {
    hello_pubsub_inproc();
    return 0;
}

结果

Starting server...
Collecting stock information from the server.

我也曾尝试在加入线程之前添加此代码:

zmq_proxy(publisher, subscriber, NULL);

解决方法:将inproc替换为tcp可以立即解决。但是inproc是否不应该针对进程内用例?

快速研究告诉我,这不可能是bindconnect的顺序,因为该问题已在我的zmq版本中解决。

下面的示例以某种方式告诉我,我没有缺少共享上下文问题,因为它不使用任何内容:

ZeroMQ Subscribers not receiving message from Publisher over an inproc: transport class

我从Signaling Between Threads (PAIR Sockets)部分的《指南》中了解到

You can use PUB for the sender and SUB for the receiver. This will correctly deliver your messages exactly as you sent them and PUB does not distribute as PUSH or DEALER do. However, you need to configure the subscriber with an empty subscription, which is annoying.

空订阅是什么意思?

我在哪里做错了?

1 个答案:

答案 0 :(得分:1)

  

您可以将PUB用于发送方,将SUB用于接收方。这样可以正确发送邮件,就像发送邮件时一样,并且PUB不会像PUSHDEALER那样分发。但是,您需要为订阅服务器配置空订阅,这很烦人。


  

Q 空订阅是什么意思?

这意味着要设置(配置)订阅,以使用空的订阅字符串驱动主题列表邮件传递过滤。

  

Q 我在哪里做错了?

这里:

// sub_conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, 0, 0);   // Wrong
   sub_conn = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "",0);  //  Empty string

有关使用正确语法和命名规则的问题,也在这里:

// int pub_conn = zmq_bind(publisher, "inproc://*:4040");
   int pub_conn = zmq_bind(publisher, "inproc://<aStringWithNameMax256Chars>");

作为 inproc:// 的传输类不使用任何类型的外部堆栈,而是将AccessPoint的I / O映射到1个以上的内存位置(无堆栈, I / O线程不需要传输类)。

鉴于此,“ <address>:<port#>”之类的东西没有被这种(这里缺少)的协议解释,因此类似字符串的文本被原样用于标识消息数据在哪个存储位置。进入。

因此,“ inproc://*:4040 ”不会扩展,而是“直译”用作命名为inproc://的传输类I / O内存位置,标识为 [*:4040] (接下来,要求使用{strong> .connect().connect( "inproc://localhost:4040" )方法将在词法上错过准备好的Memory-位置: ["*:4040"] ,因为字符串不匹配

因此,此操作应该无法.connect()-错误处理可能会保持沉默,因为自版本+ 4.x以来,没有必要遵循第一个.bind()的历史要求(创建一个“ inproc://的“ named-Memory-Location”之前可能会调用.connect()使其与“已经存在”的named-Memory-location交叉连接,因此v4.0 +很有可能在调用并创建一个不同的.bind( "inproc://*:4040" )着陆区并随后询问不匹配的.connect( "inproc://localhost:4040" )(在现有的命名内存中没有“先前准备好的”着陆区)时,不会引发任何错误-位置。