最简单的方法可能是分配一块内存来存储所有内容。例如,考虑以下结构体:
typedef struct A {
int v;
char* str;
} our_struct_t;
现在,最简单的方法是创建一个定义好的格式并将其打包成字节数组。我会尝试举个例子:
int sLen = 0;
int tLen = 0;
char* serialized = 0;
char* metadata = 0;
char* xval = 0;
char* xstr = 0;
our_struct_t x;
x.v = 10;
x.str = "Our String";
sLen = strlen(x.str); // Assuming null-terminated (which ours is)
tLen = sizeof(int) + sLen; // Our struct has an int and a string - we want the whole string not a mem addr
serialized = malloc(sizeof(char) * (tLen + sizeof(int)); // We have an additional sizeof(int) for metadata - this will hold our string length
metadata = serialized;
xval = serialized + sizeof(int);
xstr = xval + sizeof(int);
*((int*)metadata) = sLen; // Pack our metadata
*((int*)xval) = x.v; // Our "v" value (1 int)
strncpy(xstr, x.str, sLen); // A full copy of our string
所以这个例子将数据复制到一个大小为2 * sizeof(int) + sLen
的数组中,这允许我们有一个整数元数据(即字符串长度)和从结构体中提取的值。要反序列化,你可以想象以下过程:
char* serialized = // Assume we have this
char* metadata = serialized;
char* yval = metadata + sizeof(int);
char* ystr = yval + sizeof(int);
our_struct_t y;
int sLen = *((int*)metadata);
y.v = *((int*)yval);
y.str = malloc((sLen + 1) * sizeof(char)); // +1 to null-terminate
strncpy(y.str, ystr, sLen);
y.str[sLen] = '\0';
你可以看到,我们的字节数组是定义良好的。下面我详细介绍一下结构:
- 0-3 字节:元数据(字符串长度)
- 4-7 字节:X.v(数值)
- 8 - sLen 字节:X.str(数值)
这种定义明确的结构使得你可以在任何环境中重新创建该结构,只要你遵循了定义好的约定。现在,发送这个结构通过套接字取决于你如何开发协议。你可以先发送一个整数数据包,其中包含刚刚构建的数据包的总长度,或者你可以期望元数据先被发送/分离(逻辑上分离,技术上仍然可以同时发送),然后你就知道在客户端需要接收多少数据。例如,如果我接收到元数据值为10
,那么我就可以期望后续sizeof(int)+10
字节来完成该结构。通常来说,这可能是14
字节。
编辑
我将列出一些评论中要求的澄清说明。
我对字符串进行了完整的复制,因此它在(逻辑上)连续的内存中。也就是说,我序列化数据包中的所有数据实际上都是完整的数据 - 没有指针。这样,我们可以通过套接字发送一个单一缓冲区(我们将其称为serialized
)。如果仅仅发送指针,接收到指针的用户会期望该指针是一个有效的内存地址。但是,你的内存地址很可能不会完全相同。然而,即使它们相同,他也不会在该地址具有与你完全相同的数据(除非在非常有限和专业的情况下)。
希望通过查看反序列化过程(这是在接收方的端口)来更清楚地说明这一点。注意,我分配了一个结构体来保存发送方发送的信息。如果发送方没有向我发送完整的字符串,而仅仅是发送了内存地址,那么我就无法重建被发送的数据(即使在同一台机器上,我们也拥有两个不同的虚拟内存空间,它们并不相同)。因此,在本质上,指针只是一个适用于原始数据发送者的好映射。
最后,关于“嵌套结构体”,您需要为每个结构体编写多个函数。也就是说,您可以重复使用这些函数。例如,如果我有两个结构体A
和B
,其中A
包含B
,那么我可以有两个序列化方法:
char* serializeB()
{
}
char* serializeA()
{
char* B = serializeB();
}
因此,您应该能够使用每个结构体的单个序列化方法。
struct A
有一个指向struct B
的指针,但是分配时好像struct B
就放在了struct A
的后面?如果你真的想这样分配,那么你至少应该设置obj->p = (void *)((char *)obj + sizeof(struct A))
。这还不算你的解决方案本身存在的一般性错误。 - Shahbaz