【问题标题】:Serialization of struct结构体的序列化
【发布时间】:2013-05-08 18:05:55
【问题描述】:

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

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

【问题讨论】:

标签: c++ serialization deserialization winsock2


【解决方案1】:

以下示例显示了将struct 序列化为char 数组并对其进行反序列化的最简单方法。

#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;
}

【讨论】:

  • 简单序列化的好例子
  • 您的代码有两个内存泄漏。实际上根本不需要使用new。以下是进一步可能的改进: 1. 使 MSG 成为反序列化函数的返回类型而不是输出参数 2. 代替 typedef struct MSG{...}MSG; 使用 struct MSG{...};,在 C++ 中相同 3. 使用 std::stringstd::vector&lt;byte&gt;保存序列化数据(并完全处理 #define PACKETSIZE)并将其用作 serialize 函数的返回值而不是输出参数(或者如果您坚持将参数更改为 char(&amp;data)[PACKETSIZE])。
  • 这假定发送者和接收者都知道结构的成员。如果结构的每个成员的细节也被嵌入和发送,接收者可以使用它来构建适当的指针。一端的简单更改而另一端不知道会导致很多错误
  • 这是一个很好的简单实现,真的。但是,在某些情况下,这种实现肯定不起作用:1)如果发送者和接收者没有相同的结构实现(正如@AlphaGoku 指出的那样),2)如果编译器或编译器标志不同(例如,一个编译器决定删除结构元素之间的填充字节,或者,如果int 在一个编译器中是 4 个字节,而在另一个编译器中是 2 个字节),或者,3)更阴险,如果发送方和接收方不具有相同的字节序(例如,发送方是小端,接收方是大端)。
  • @AndrewBloom 我同意你的观点,这些问题可以解决(否则不同的机器将无法通过网络、总线或其他方式进行通信!)。我的意思是这个答案只给出了代码而不是它背后的逻辑。就目前的代码而言,它遇到了我解释的问题。
【解决方案2】:

你可以这样做

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;
    }
};

您可以自行处理 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.

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

      在接待处反之亦然:

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

      其中datachar*。不要忘记你必须分配它,确保它足够大并在最后删除它。

      【讨论】:

      • 在发送端:struct example{ int a;浮动 b;双c; };例如前;例如a = 1;例如b = 1.02; ex.c = 1.040; ::memcpy(缓冲区, &ex, sizeof(example)); retval = 发送(conn_socket,缓冲区,sizeof(缓冲区),0);而在接收端:例如 ex; retval = recvfrom(msgsock,Buffer, sizeof(Buffer), 0, (struct sockaddr *)&from, &fromlen); ::memcpy(&ex, 缓冲区, sizeof(example)); printf("%f", ex.c);输出:-62774385622041925000000000000000000000000000000。这是为什么呢?
      • 仅在内存复制结构时需要非常小心,因为不同的架构和不同的编译器会以不同的方式应用填充和字节序。
      • 我在两个系统上都使用 Visual Studio 2005 和 XP。但为什么会有这种差异?
      • 你的缓冲区来自哪里?你确定它比你的结构大吗?
      • 是的..我的缓冲区有 60 个字节的 malloc-ed,而结构很容易小于 60..
      【解决方案5】:

      好的,我将使用 boost 网站上的 example,因为我不明白您从中无法理解的内容。
      我添加了一些 cmets 并对其进行了更改,您可以如何通过网络进行传输。网络代码本身不在此处。为此,您可以查看boost::asio

      int main() {
          // create and open a character archive for output
          // we simply use std::strinstream here
          std::stringstream ofs;
      
          // create class instance
          const gps_position g(35, 59, 24.567f);
      
          // save data to archive
          {
              boost::archive::text_oarchive oa(ofs);
              // write class instance to archive
              oa << g;
              // archive and stream closed when destructors are called
          }
      
          // now we have const char* ofs.str().c_str()
          // transfer those bytes via network
          // read them on the other machine
      
          gps_position newg;
          {
              // create and open an archive for input
              std::stringstream ifs(the_string_we_read_from_the_network);
              boost::archive::text_iarchive ia(ifs);
              // read class state from archive
              ia >> newg;
              // archive and stream closed when destructors are called
          }
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多