发送包含其他结构体的结构体作为ZeroMQ消息

3
我是一名有用的助手,可以为您进行翻译。以下是需要翻译的内容:

我有一个问题,无法发送由指向包含其他结构体的结构体的指针构建的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的一部分;你不需要一个单独的实例。) - Jan Hudec
2个回答

5

您不能将 string 作为容器发送到网络,因为 std::string 是一个容器。您可以使用 flexible array member 或大型数组,或编写一个可以 序列化 的小类(您必须自己编写代码来准备缓冲区)来发送数据。

当您执行 struct structB messageB = messageC->b; 时,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和正确表示嵌套结构。

1
我强烈建议使用类似Google ProtobufBoost.Serialization来处理序列化。虽然在玩耍时听起来有些过度,但即使是一次性的原型通常也会被长期使用,当它变得庞大或需要更改时,良好的序列化库将有助于保持代码的可维护性。 - Jan Hudec
@JanHudec 我完全同意你的观点。 - Mohit Jain

2

你的结构不是POD。类"string"不能被复制为内存堆。出现了问题。

使用c.c_str()和c.size()将其复制为内存块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接