我正在编写一些代码来序列化一些数据以通过网络发送它。目前,我使用这个原始程序:
void*
缓冲区hton
系列memcpy
将内存复制到缓冲区问题在于,对于各种数据结构(通常包含void *数据,因此您不知道是否需要关心字节排序),代码变得非常臃肿,序列化代码非常特定于每个数据结构,根本不能重复使用。
什么是C的一些好的序列化技术,使这更容易/更少丑陋?
-
注意:我必须使用特定协议,因此无法自由选择如何序列化数据。
答案 0 :(得分:32)
对于每个数据结构,都有一个serialize_X函数(其中X是结构名称),它接受指向X的指针和指向不透明缓冲区结构的指针,并调用相应的序列化函数。您应该提供一些原语,例如serialize_int,它们写入缓冲区并更新输出索引。 原语必须调用诸如reserve_space(N)之类的东西,其中N是在写入任何数据之前所需的字节数。 reserve_space()将重新分配void *缓冲区,使其至少与当前大小加上N个字节一样大。 为了实现这一点,缓冲区结构将需要包含指向实际数据的指针,将下一个字节写入的索引(输出索引)以及为数据分配的大小。 使用此系统,所有serialize_X函数都应该非常简单,例如:
struct X {
int n, m;
char *string;
}
void serialize_X(struct X *x, struct Buffer *output) {
serialize_int(x->n, output);
serialize_int(x->m, output);
serialize_string(x->string, output);
}
框架代码将类似于:
#define INITIAL_SIZE 32
struct Buffer {
void *data;
int next;
size_t size;
}
struct Buffer *new_buffer() {
struct Buffer *b = malloc(sizeof(Buffer));
b->data = malloc(INITIAL_SIZE);
b->size = INITIAL_SIZE;
b->next = 0;
return b;
}
void reserve_space(Buffer *b, size_t bytes) {
if((b->next + bytes) > b->size) {
/* double size to enforce O(lg N) reallocs */
b->data = realloc(b->data, b->size * 2);
b->size *= 2;
}
}
由此,实现所需的所有serialize_()函数应该非常简单。
编辑: 例如:
void serialize_int(int x, Buffer *b) {
/* assume int == long; how can this be done better? */
x = htonl(x);
reserve_space(b, sizeof(int));
memcpy(((char *)b->data) + b->next, &x, sizeof(int));
b->next += sizeof(int);
}
编辑: 另请注意,我的代码有一些潜在的错误。缓冲区数组的大小存储在size_t中,但索引是一个int(我不确定size_t是否被认为是索引的合理类型)。此外,没有提供错误处理,也没有在完成后释放缓冲区的功能,因此您必须自己完成此操作。我只是演示了我将使用的基本架构。
答案 1 :(得分:5)
我想说绝对不要尝试自己实现序列化。它已经完成了数万次,您应该使用现有的解决方案。例如protobufs:https://github.com/protobuf-c/protobuf-c
它还具有与许多其他编程语言兼容的优势。
答案 2 :(得分:5)
我建议使用图书馆。
由于我对现有的库感到不满意,我创建了Binn库以使我们的生活更轻松。
以下是使用它的示例:
binn *obj;
// create a new object
obj = binn_object();
// add values to it
binn_object_set_int32(obj, "id", 123);
binn_object_set_str(obj, "name", "Samsung Galaxy Charger");
binn_object_set_double(obj, "price", 12.50);
binn_object_set_blob(obj, "picture", picptr, piclen);
// send over the network
send(sock, binn_ptr(obj), binn_size(obj));
// release the buffer
binn_free(obj);
答案 3 :(得分:2)
如果我们知道协议约束是什么会有所帮助,但总的来说,你的选择非常有限。如果数据是这样的,你可以为每个结构建立一个字节数组sizeof(struct)的联合,它可能会简化一些事情,但是从你的描述中听起来你有一个更基本的问题:如果你正在转移指针(你提到) void * data)那么这些点在接收机器上不太可能有效。为什么数据恰好出现在内存中的同一个地方?
答案 4 :(得分:0)
对于“ C”程序,当“自动”序列化没有很多好的选择时。在“放弃”之前,建议检查一下SUNRPC软件包(rpcgen和朋友)。它具有:
协议和代码具有Internet标准。