结构的序列化

Vig*_*igo 11 c++ serialization winsock2 deserialization

假设我有一个结构,其成员值我想通过网络发送到使用winsock 2的另一个系统.我正在使用C++语言.我如何将其转换为char*记住结构必须在发送之前被序列化,以及如何将char*反序列化为另一端的struct?我发现boost序列化是对类似问题的建议,但任何人都可以通过一个小的代码片段来说明序列化和反序列化吗?

这个问题可能看起来很基本,但相关帖子的其他答案并没有多大帮助.

Ami*_*aka 16

以下示例显示了序列化structchar数组并对其进行反序列化的最简单方法.

#include <iostream>
#include <cstring>

#define BUFSIZE 512
#define PACKETSIZE sizeof(MSG)

using namespace std;

typedef struct MSG
{
    int type;
    int priority;
    int sender;
    char message[BUFSIZE];
}MSG;

void serialize(MSG* msgPacket, char *data);
void deserialize(char *data, MSG* msgPacket);
void printMsg(MSG* msgPacket);

int main()
{
    MSG* newMsg = new MSG;
    newMsg->type = 1;
    newMsg->priority = 9;
    newMsg->sender = 2;
    strcpy(newMsg->message, "hello from server\0");
    printMsg(newMsg);

    char data[PACKETSIZE];

    serialize(newMsg, data);

    MSG* temp = new MSG;
    deserialize(data, temp);
    printMsg(temp);

    return 0;
}

void serialize(MSG* msgPacket, char *data)
{
    int *q = (int*)data;    
    *q = msgPacket->type;       q++;    
    *q = msgPacket->priority;   q++;    
    *q = msgPacket->sender;     q++;

    char *p = (char*)q;
    int i = 0;
    while (i < BUFSIZE)
    {
        *p = msgPacket->message[i];
        p++;
        i++;
    }
}

void deserialize(char *data, MSG* msgPacket)
{
    int *q = (int*)data;    
    msgPacket->type = *q;       q++;    
    msgPacket->priority = *q;   q++;    
    msgPacket->sender = *q;     q++;

    char *p = (char*)q;
    int i = 0;
    while (i < BUFSIZE)
    {
        msgPacket->message[i] = *p;
        p++;
        i++;
    }
}

void printMsg(MSG* msgPacket)
{
    cout << msgPacket->type << endl;
    cout << msgPacket->priority << endl;
    cout << msgPacket->sender << endl;
    cout << msgPacket->message << endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 简单序列化的好例子 (2认同)
  • 您的代码有两个内存泄漏。实际上根本没有必要使用“new”。以下是进一步可能的改进: 1. 将 MSG 设置为反序列化函数的返回类型,而不是输出参数 2. 使用 `struct MSG{...};` 代替 `typedef struct MSG{...}MSG;` ,在 C++ 中也是一样的 3. 使用 `std::string` 或 `std::vector&lt;byte&gt;` 来保存序列化数据(并完全处理 `#define PACKETSIZE`)并将其用作使用 `serialize` 函数而不是输出参数(或者如果您坚持将参数更改为 `char(&amp;data)[PACKETSIZE]`)。 (2认同)
  • 这假设发送者和接收者都知道该结构的成员。如果结构体每个成员的详细信息也被嵌入和发送,则接收者可以使用它来构建适当的指针。一端的一个简单更改而另一端不知道它会导致很多错误 (2认同)
  • 这是一个很好的简单实现,确实如此。但是,在某些情况下,这种实现肯定不起作用:1)如果发送方和接收方没有相同的结构实现(如 @AlphaGoku 指出的),2)如果编译器或编译器标志不同(例如,一个编译器决定删除结构元素之间的填充字节,或者,如果“int”在一个编译器中为 4 个字节,在另一个编译器中为 2 个字节),或者,3)如果发送方和接收方没有相同的字节序,则更加阴险(例如,发送方是小端字节序,接收方是大端字节序)。 (2认同)

rwo*_*ols 5

你可以做

struct MyStruct {

    int data;
    char* someNullTerminatedName; // Assuming not larger than 1023 chars

    std::ostream& serialize(std::ostream& os) const {
        char null = '\0';
        os.write((char*)&data, sizeof(data));
        os.write(someNullTerminatedName, strlen(someNullTerminatedName));
        os.write(&null, 1);
        return os;
    }
    std::istream& deserialize(std::istream& is) {
        char buffer[1024];
        int i = 0;
        is.read((char*)&data, sizeof(data));
        do { buffer[i] = is.get(); ++i; } while(buffer[i] != '\0');
        if (someNullTerminatedName != NULL) free(someNullTerminatedName);
        someNullTerminatedName = (char*)malloc(i);
        for (i = 0; buffer[i] != '\0'; ++i) {
            someNullTerminatedName[i] = buffer[i];
        }
        return is;
    }
};
Run Code Online (Sandbox Code Playgroud)

由您自己决定字节序和ints 大小的差异以及诸如此类。

例:

MyStruct foo, bar;
std::stringstream stream;
foo.serialize(stream);
// ... Now stream.str().c_str() contains a char* buffer representation of foo.
// For example it might contain [ 1f 3a 4d 10 h e l l o w o r l d \0 ]
bar.deserialize(stream);
// ... Now bar is a copy, via a serial stream of data, of foo.
Run Code Online (Sandbox Code Playgroud)

如果您有一个通过C ++ iostream公开其接口的套接字库,那么您甚至不需要stringstream。


Nic*_*ick 5

您还可以查看Google 的Protocol Buffers,它是一个独立于平台/语言的库,用于在主机之间发送数据。

然而,范式转向首先编写协议,然后将您的数据结构放入其中。这样做的好处是它迫使你的软件架构很好地适应简单的数据类型。


Art*_*hur 4

如果您的结构是POD您可以使用memcpy

::memcpy(data, &your_struct, sizeof(YourStruct)));

反之亦然,在接待处:

::memcpy(&your_struct, data, sizeof(YourStruct)));

哪里data有一个char*. 不要忘记您必须分配它,确保它足够大并在最后删除它。

  • 当仅内存复制结构时,您需要非常小心,因为不同的体系结构和不同的编译器将以不同的方式应用填充和字节序。 (9认同)