C - 序列化技术

时间:2011-05-14 14:43:54

标签: c serialization

我正在编写一些代码来序列化一些数据以通过网络发送它。目前,我使用这个原始程序:

  1. 创建void*缓冲区
  2. 对我想通过网络发送的数据应用任何字节排序操作,例如hton系列
  3. 使用memcpy将内存复制到缓冲区
  4. 通过网络发送内存
  5. 问题在于,对于各种数据结构(通常包含void *数据,因此您不知道是否需要关心字节排序),代码变得非常臃肿,序列化代码非常特定于每个数据结构,根本不能重复使用。

    什么是C的一些好的序列化技术,使这更容易/更少丑陋?

    -

    注意:我必须使用特定协议,因此无法自由选择如何序列化数据。

5 个答案:

答案 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和朋友)。它具有:

  • 自定义格式,用于描述数据结构的“ XDR”语言(基本上是“ C”的子集)。
  • RPC生成-可以自动生成序列化的客户端和服务器端。
  • 运行时库,(几乎)所有unix环境都随附。

协议和代码具有Internet标准。