You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用ENet库与memcpy发送含可变长度数组的结构体?

关于ENet发送含可变长度数组结构体的问题及解决方法

问题描述

我正在使用ENet库与memcpy进行序列化操作,想请教如何发送包含可变长度数组的结构体?以下是我的客户端与服务端代码:

客户端代码

struct data{
  enum type type;
  int* data;
};

int world[10];
for(int i =0;i<10;i++)
{
    world[i] = i;
}

struct data dt;
dt.data = malloc(sizeof(int)*10);
dt.type = 1;

for(int i =0;i<10;i++)
{
    dt.data[i] = i;
}

char msg[1024];

memcpy(msg, &dt, sizeof(dt));

ENetPacket *pack = enet_packet_create(msg, sizeof(msg)+1, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(event.peer, 0, pack);

服务端代码

ENET_EVENT_TYPE_RECEIVE:
        
            struct data copy_data;
            copy_data.data = (int*)malloc(10*sizeof(int));

            memcpy(&copy_data, event.packet->data, sizeof(struct data));

            for(int i = 0;i<10;i++)
            {
                printf("%d", copy_data.data[i]);
            }

请问我的代码存在什么问题?正确发送含可变长度数组的结构体的方法是什么?


代码核心问题

  • 指针地址无效:结构体中的int* data是客户端本地内存的指针地址,memcpy发送的只是这个地址值,服务端收到后该地址在自身内存空间完全无效,直接访问会触发崩溃、乱码等未定义行为。
  • 未发送数组实际内容:你只拷贝了结构体本身,完全没把数组的真实数据放进发送缓冲区,服务端自然拿不到有效数组内容。
  • 硬编码长度不灵活:服务端固定malloc10个int的空间,无法适配客户端数组长度变化的场景,违背了“可变长度”的需求。
  • 数据包长度错误:客户端用sizeof(msg)+1作为数据包长度,但实际有效数据只有结构体大小,多余的无效字节会浪费带宽,也可能干扰解析。

正确实现方法

要发送含可变长度数组的结构体,必须把结构体元数据(类型、数组长度)和数组实际内容拼接成连续字节流发送,服务端按顺序解析即可。

客户端实现

// 重新定义结构体,新增数组长度字段
struct data{
    enum type type;
    int length; // 记录数组的元素个数
};

int main() {
    int length = 10;
    int* dt_data = malloc(length * sizeof(int));
    if (!dt_data) { /* 处理内存分配失败 */ }
    for(int i = 0; i < length; i++) {
        dt_data[i] = i;
    }

    struct data dt;
    dt.type = 1;
    dt.length = length;

    // 计算总数据包大小:结构体字节数 + 数组总字节数
    size_t total_size = sizeof(struct data) + length * sizeof(int);
    char* msg = malloc(total_size);
    if (!msg) { /* 处理内存分配失败 */ }

    // 先拷贝结构体到缓冲区开头
    memcpy(msg, &dt, sizeof(struct data));
    // 再拷贝数组内容到结构体后面的位置
    memcpy(msg + sizeof(struct data), dt_data, length * sizeof(int));

    // 创建并发送数据包
    ENetPacket *pack = enet_packet_create(msg, total_size, ENET_PACKET_FLAG_RELIABLE);
    enet_peer_send(event.peer, 0, pack);

    // 释放本地内存,ENet会自行复制数据包内容
    free(dt_data);
    free(msg);
}

服务端实现

ENET_EVENT_TYPE_RECEIVE:
    // 第一步:解析结构体元数据
    struct data copy_data;
    memcpy(&copy_data, event.packet->data, sizeof(struct data));

    // 验证长度合理性,防止恶意数据导致内存溢出
    if (copy_data.length <= 0 || (size_t)copy_data.length * sizeof(int) > event.packet->dataLength - sizeof(struct data)) {
        // 非法数据,直接丢弃
        enet_packet_destroy(event.packet);
        break;
    }

    // 第二步:根据长度分配内存并拷贝数组内容
    int* data = malloc(copy_data.length * sizeof(int));
    if (!data) { /* 处理内存分配失败 */ }
    memcpy(data, event.packet->data + sizeof(struct data), copy_data.length * sizeof(int));

    // 使用数组数据
    for(int i = 0; i < copy_data.length; i++) {
        printf("%d ", data[i]);
    }

    // 释放资源
    free(data);
    enet_packet_destroy(event.packet);

额外注意事项

  • 字节序兼容:如果客户端和服务端运行在不同架构的机器上(如x86和ARM),需要用htonl/ntohl将整数类型(type、length、数组元素)转换为网络字节序,避免数据错乱。
  • 边界检查:服务端必须验证收到的length是否在合理范围,防止攻击者发送超大长度值导致内存耗尽或缓冲区溢出。

内容的提问来源于stack exchange,提问作者JustOneMan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 03:55:35