基于TCP/IP的frame结构体收发函数故障排查求助
问题分析与解决方案
看起来你踩了TCP序列化包含指针的结构体的经典坑——直接把结构体强转为char*发送的话,你发送的只是指针的内存地址,而不是指针指向的实际数据。接收端拿到这个地址完全没用,因为两端的内存空间是完全独立的,接收端的这个地址要么是无效的,要么指向完全无关的数据。
核心问题拆解
你的struct frame里的int* body是指针,当你直接把&f强转为char*时,只会把length的值、body的地址、tail的值这三个东西打包发送,而body指向的数组内容根本没被发出去。这就是你的函数无法正常工作的根本原因。
正确的序列化/反序列化实现
要解决这个问题,你需要手动把结构体里的所有实际数据(包括body指向的数组)打包到char数组里发送,接收端再把这些数据重新组装成结构体。
发送端修正代码
struct frame { int length; int * body; int tail; }; void winsock_client::send_frame(frame f) { // 计算需要发送的总字节数:length(4字节) + body的总字节数 + tail(4字节) int total_bytes = sizeof(f.length) + f.length * sizeof(int) + sizeof(f.tail); char *arr = new char[total_bytes]; char *ptr = arr; // 用指针来遍历数组,逐个拷贝数据 // 1. 序列化length字段 memcpy(ptr, &f.length, sizeof(f.length)); ptr += sizeof(f.length); // 2. 序列化body指向的实际数组数据 memcpy(ptr, f.body, f.length * sizeof(int)); ptr += f.length * sizeof(int); // 3. 序列化tail字段 memcpy(ptr, &f.tail, sizeof(f.tail)); // 发送数据,注意要处理TCP的部分发送情况 int bytes_sent = 0; while (bytes_sent < total_bytes) { int sent = send(sock, arr + bytes_sent, total_bytes - bytes_sent, 0); if (sent == SOCKET_ERROR) { std::cerr << "Send failed with error: " << WSAGetLastError() << std::endl; break; } bytes_sent += sent; } delete[] arr; // 记得释放动态分配的内存 }
接收端反序列化代码
frame receive_frame(SOCKET sock) { frame f{}; char temp_buf[sizeof(int)]; // 1. 先接收length字段,确定body的长度 int bytes_recv = recv(sock, temp_buf, sizeof(f.length), 0); if (bytes_recv != sizeof(f.length)) { // 接收错误或数据不完整,标记为无效frame f.length = -1; return f; } memcpy(&f.length, temp_buf, sizeof(f.length)); // 2. 为body分配内存 f.body = new int[f.length]; if (!f.body) { f.length = -1; return f; } // 3. 接收body的实际数据 bytes_recv = recv(sock, reinterpret_cast<char*>(f.body), f.length * sizeof(int), 0); if (bytes_recv != f.length * sizeof(int)) { // 接收失败,释放已分配的内存 delete[] f.body; f.length = -1; return f; } // 4. 接收tail字段 bytes_recv = recv(sock, temp_buf, sizeof(f.tail), 0); if (bytes_recv != sizeof(f.tail)) { delete[] f.body; f.length = -1; return f; } memcpy(&f.tail, temp_buf, sizeof(f.tail)); return f; }
额外注意事项
- 字节序问题:如果你的客户端和服务端运行在不同平台(比如Windows和Linux),要注意整数的字节序差异,需要用
htonl()/ntohl()把整数转换为网络字节序后再发送,接收时再转换回来。 - TCP粘包:如果连续发送多个frame,接收端可能会收到粘在一起的数据。解决办法是先发送整个frame的总长度,接收时先读取总长度,再读取对应长度的数据,这样就能正确拆分每个frame。
- 内存泄漏:接收端使用完
frame后,一定要调用delete[] f.body释放内存,避免内存泄漏。 - 错误处理:示例中的错误处理比较基础,实际项目中可以根据需求添加重发、断开连接等逻辑。
内容的提问来源于stack exchange,提问作者Andy Lin
相关产品推荐
相关产品推荐

