【问题标题】:Sending struct containing other struct as ZeroMQ message将包含其他结构的结构作为 ZeroMQ 消息发送
【发布时间】:2014-07-03 11:49:20
【问题描述】:

我在发送从指向结构的指针构建的 zmq 消息时遇到问题,该结构包含其他结构。

服务器代码:

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

using namespace zmq;
using namespace std;

struct structB{
    int a;
    string c;
};

struct structC{
    int z;
    struct structB b;
};

int main()
{
    context_t context(1);
    socket_t *socket = new socket_t(context,ZMQ_REP);
    socket->bind("tcp://*:5555");

    message_t *request = new message_t();   
    socket->recv(request);

    struct structB messageB; 
    messageB.a=0;
    messageB.c="aa";

    struct structC *messageC = new struct structC;
    messageC->z = 4;
    messageC->b = messageB;

    char *buffer = (char*)(messageC);
    message_t *reply = new message_t((void*)buffer,
                       +sizeof(struct structB)
                       +sizeof(struct structC)
                       ,0);
    socket->send(*reply);

    return 0;
}

客户端代码:

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

using namespace std;
using namespace zmq;

struct structB{
    int a;
    string c;
};

struct structC{
    int z;
    struct structB b;
};

int main()
{ 
    context_t context(1);
    socket_t *socket = new socket_t(context,ZMQ_REQ);
    socket->connect("tcp://*:5555");

    const char* buffer = "abc";
    message_t *request = new message_t((void*)buffer,sizeof(char*),0);
    socket->send(*request);

    message_t *reply = new message_t;
    socket->recv(reply);

    struct structC *messageC = new struct structC;
    messageC = static_cast<struct structC*>(reply->data());

cout<<messageC->b.a<<endl;//no crash here

    struct structB messageB = messageC->b;//Segmentation fault (core dumped)

    return 0;
}

当我尝试使用 structB 中名为“c”的字符串时,该程序崩溃。如果我尝试打印它,或者像上面的例子那样分配整个 structB 都没关系。

问题出在哪里?我应该以不同的方式在服务器端创建 message_t *reply 吗?

【问题讨论】:

  • 你奇怪地混合了 C 和 C++ 语法(在 C 中,结构是单独的命名空间,并且在使用时需要 struct 关键字,但在 C++ 中却不需要。为什么有时 使用它?)以及堆栈和堆分配对象的类似奇怪组合(为什么你在堆栈上创建messageB 而在堆上创建messageCmessageC 也可以在堆栈上创建。messageBmessageC 的一部分;您不需要单独的实例。)

标签: c++ arrays struct zeromq


【解决方案1】:

您的结构不是 POD。类“字符串”不能复制为内存堆。有问题。

使用 c.c_str() 和 c.size() 作为内存块拷贝进去。

【讨论】:

    【解决方案2】:

    您不能通过网络发送string,因为std::string 是一个容器。你可以使用flexible array member或者大数组或者写一个小类serializable(你必须自己写代码准备缓冲区)来发送数据。

    当您执行struct structB messageB = messageC-&gt;b; 时,嵌入在messageC->b 中的std::string 成员的指针成员可能在复制构造函数或std::string 中被取消引用,这可能导致分段错误。

    大字符数组的示例是:

    struct structB{
        int a;
        char c[MAX_LENGTH];
    };
    

    稍后

    struct structB messageB; 
    messageB.a=0;
    strcpy(messageB.c,"aa"); // #include<cstring> or use std::copy from <algorithm>
    
    struct structC *messageC = new struct structC;
    // I guess you want this(not sure) messageC->z = static_cast<int>( sizeof(int) + strlen(messageB) + 1 );
    messageC->z = 4;
    messageC->b = messageB;
    

    然后

    const int length = sizeof(int) /* z */ + sizeof(int) /* a */ + strlen("aa") + 1;
    zmq::message_t msg (length);
    memcpy (msg.data (), &messageC, length);
    socket->send(msg);
    

    这些是服务器端需要的一些更改,您也需要在客户端进行类似的更改。

    附带说明一下,您的代码非常混乱,在整理出一些事情之前不要将其部署在更大的应用程序中,例如删除不必要的 new 并正确表示嵌套结构。

    【讨论】:

    • 我强烈建议使用Google ProtobufBoost.Serialization 之类的东西来处理序列化。这听起来有点矫枉过正,但即使是扔掉的原型也往往会在超过最初预期的生命周期后被使用,当它变大或需要更改时,好的序列化库将有助于保持代码的可维护性。
    • @JanHudec 我完全同意你的看法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-06
    • 1970-01-01
    相关资源
    最近更新 更多