如何使用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(©_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(©_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
相关产品推荐
相关产品推荐

