如何在c中序列化结构?
我有一个struct对象,它由几个基本数据类型、指针和结构指针组成。我想把它通过插座发送,这样它就可以在另一端使用了。由于我想预先支付序列化成本,如何初始化该结构的对象,以便在不进行编组的情况下立即发送它?比如说如何在c中序列化结构?,c,networking,C,Networking,我有一个struct对象,它由几个基本数据类型、指针和结构指针组成。我想把它通过插座发送,这样它就可以在另一端使用了。由于我想预先支付序列化成本,如何初始化该结构的对象,以便在不进行编组的情况下立即发送它?比如说 struct A { int i; struct B *p; }; struct B { long l; char *s[0]; }; struct A *obj; // can do I initialize obj? int len =
struct A {
int i;
struct B *p;
};
struct B {
long l;
char *s[0];
};
struct A *obj;
// can do I initialize obj?
int len = sizeof(struct A) + sizeof(struct B) + sizeof(?);
obj = (struct A *) malloc(len);
...
write(socket, obj, len);
// on the receiver end, I want to do this
char buf[len];
read(socket, buf, len);
struct A *obj = (struct A *)buf;
int i = obj->i;
char *s = obj->p->s[0];
int i obj.i=1; obj.p.
谢谢。@Shahbaz是对的,我想你真的想要这个
int len = sizeof(struct A);
obj = (struct A *) malloc(len);
但是,当您将指针发送到另一台机器时,您也会遇到问题,因为指针指向的地址在另一台机器上没有任何意义。除了您的
malloc
的问题之外,这个答案也有问题
不幸的是,您无法找到一个仍然与标准兼容的好技巧。正确序列化结构的唯一方法是分别将每个元素分解为字节,将它们写入无符号字符数组,通过网络发送,然后将这些片段重新组合到另一端。简而言之,您将需要大量移位和按位操作
在某些情况下,您需要定义一种协议。例如,在您的示例中,您需要确保始终将对象p
指向struct A
的正后方,以便恢复后,可以正确设置指针。每个人都说你们不能通过网络发送指针了吗
您可能要做的另一件协议操作是将分配给灵活数组成员s
的大小写入struct B
。无论您为序列化数据选择何种布局,显然双方都应该尊重
需要注意的是,您不能依赖任何特定于机器的东西,例如字节顺序、结构填充或基本类型的大小。这意味着您应该分别序列化元素的每个字段,并为它们分配固定数量的字节。最简单的方法可能是分配一块内存来保存所有内容。例如,考虑如下结构:
typedef struct A {
int v;
char* str;
} our_struct_t;
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';
现在,最简单的方法是创建定义的格式并将其打包到字节数组中。我将尝试展示一个示例:
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
的数组中,该数组允许我们使用元数据的单个整数(即字符串长度)和从结构中提取的值。要反序列化,您可以想象如下:
typedef struct A {
int v;
char* str;
} our_struct_t;
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
字节
编辑
我将按照评论中的要求列出一些澄清
我对字符串进行完整复制,使其位于(逻辑)连续内存中。也就是说,我的序列化数据包中的所有数据实际上都是完整数据——没有指针。通过这种方式,我们可以通过套接字发送单个缓冲区(我们称之为序列化的)。如果只是发送指针,则接收指针的用户将期望该指针是有效的内存地址。但是,内存地址不太可能完全相同。但是,即使他们是,他在该地址的数据也不会与您相同(除非在非常有限和特殊的情况下)
希望通过查看反序列化过程(这是在接收方方面),可以更清楚地了解这一点。注意我是如何分配一个结构来保存发送者发送的信息的。如果发送方没有向我发送完整的字符串,而是只发送内存地址,那么我实际上无法重建发送的数据(即使在同一台机器上,我们有两个不同的虚拟内存空间,它们并不相同)。所以本质上,指针只是发起者的一个好映射
最后,就“结构中的结构”而言,每个结构都需要几个函数。也就是说,您可以重用这些函数。例如,如果我有两个结构A
和B
,其中A
包含B
,我可以有两个序列化方法:
char* serializeB()
{
// ... Do serialization
}
char* serializeA()
{
char* B = serializeB();
// ... Either add on to serialized version of B or do some other modifications to combine the structures
}
因此,您应该能够为每个结构使用单一的序列化方法。解释您的数据并理解您想要序列化的内容。您想要序列化一个整数和一个类型为B的结构(递归地,您想要序列化一个int、一个long和一个字符串数组)。然后序列化它们。所需长度为sizeof(int)+sizeof(long)+∑斯特伦(s[i])+1
另一方面,序列化是一个已解决的问题(实际上是多次)。您确定需要手工编写序列化例程吗?为什么不使用D-Bus或简单的RPC调用?请考虑使用它们。 < P>应以平台无关的方式序列化数据。
以下是使用库(我的创建)的示例:
如果不想使用字符串作为键,可以使用使用整数作为键的binn_映射。还有对列表的支持。您可以在另一个结构(嵌套结构)中插入一个结构。例如:
我尝试了@RageD提供的方法,但无效
我从反序列化中获得的int
值不是t
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct A {
int a;
char *str;
} test_struct_t;
char *serialize(test_struct_t t) {
int str_len = strlen(t.str);
int size = 2 * sizeof(int) + str_len;
char *buf = malloc(sizeof(char) * (size+1));
memcpy(buf, &t.a, sizeof(int));
memcpy(buf + sizeof(int), &str_len, sizeof(int));
memcpy(buf + sizeof(int) * 2, t.str, str_len);
buf[size] = '\0';
return buf;
}
test_struct_t deserialize(char *buf) {
test_struct_t t;
memcpy(&t.a, buf, sizeof(int));
int str_len;
memcpy(&str_len, buf+sizeof(int), sizeof(int));
t.str = malloc(sizeof(char) * (str_len+1));
memcpy(t.str, buf+2*sizeof(int), str_len);
t.str[str_len] = '\0';
return t;
}
int main() {
char str[15] = "Hello, world!";
test_struct_t t;
t.a = 123;
t.str = malloc(strlen(str) + 1);
strcpy(t.str, str);
printf("original values: %d %s\n", t.a, t.str);
char *buf = serialize(t);
test_struct_t new_t = deserialize(buf);
printf("new values: %d %s\n", new_t.a, new_t.str);
return 0;
}
original values: 123 Hello, world!
new values: 123 Hello, world!