在C中通过UDP发送结构中的动态数组

Sending dynamic arrays in struct over UDP in C

我正在编写一个代码,其中我有一个结构,其中一个成员是一个动态数组(直到运行时才知道大小)。 我需要通过 UDP 连接发送此结构,并能够在另一端读取和处理数据。

我知道我可以使用指针和 malloc 来定义动态数组,但我无法通过 UDP 发送指针。我也在努力避免序列化。

结构是这样的,用户必须输入 ID 来定义消息类型以及数组中的消息。

struct foo
{
    unsigned int ID;
    unsigned int Num;
    unsigned int size;
    char arr[size]; //I know this is not acceptable in C, just typing to show my question.
};

是否有另一种方法来创建可以通过 UDP 套接字发送的动态数组?在接收方处理此问题的最佳方法是什么?

您可以定义一个具有最小数组的结构,并为具有更大数组的结构分配内存。

下面的代码示例是无效的 C,它是不完整的。当通过网络发送到另一台机器时,它也不处理 int 值可能存在的字节顺序或大小差异。

struct foo
{
    unsigned int ID;
    unsigned int Num;
    unsigned int size;
    char arr[1];
};

int size = 123;

int i;
int ret;

struct foo *ptr = malloc(offsetof(struct foo, arr) + size);

ptr->ID = 42;
ptr->Num = 99;
ptr->size = size;
for(i=0; i<ptr->size; i++)
{
     ptr->arr[i] = data;
}


ret = sendto(sockfd, ptr, offsetof(struct foo, arr) + size, flags,
           dest_addr, addrlen);

编译器不关心您访问的数组索引是否大于您在结构声明中定义的数组索引。您必须检查代码中的索引以避免内存访问错误。

接收方可以先用recvfrom()MSG_PEEK以及header的大小(offsetof(struct foo, arr))变成一个临时结构struct foo tmp;,然后根据size 字段,然后将结构的完整大小读入动态分配的内存中。另见 How to read UDP packet with variable length in C

或者使用最大大小为recvfrom()的固定缓冲区,然后将数据复制到动态分配的结构中。 recvfrom() 的 return 值将告诉您所需的大小。

根据 Matthias Simon 的回答中的建议进行编辑:

如果您的编译器支持大小为零的数组,则代码可以稍微简化。

在结构定义中,您可以使用 char arr[0]; 而不是 char arr[1];。这允许在 malloc()sendto()recvfrom() 中使用 sizeof struct foo 而不是 offsetof(struct foo, arr)MSG_PEEK.

通过 UDP 发送具有动态大小的结构没有什么特别之处。一般来说,你只需要分配足够的内存,就像博多在他的回答中描述的那样。 另外,请查看 GCC 手册:Arrays of Length Zero and Arrays of Variable Length,希望对您有所帮助。并且不要忘记 UDP 消息有大小限制。

[...] I'm trying to avoid serialization as well.

我经常看到人们避免使用编解码器,因为他们的目标是获得良好的性能。最后,由于字节顺序交换和打包问题,他们得到了相反的结果。 序列化是您的朋友,尤其是在与其他进程通信时。值得一读的是Rob Pike's article about byte-order