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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:19:27