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

C语言Socket编程recv()接收数据长度返回异常值问题排查

问题根因

你的问题核心是TCP字节流协议的短读场景未处理,同时自定义的收全数据函数实现存在逻辑错误,最终导致数据流错位,解析出异常长度值,具体问题点如下:

  1. 长度头部接收逻辑错误
    你调用recv(client_sock, size_message, 4, 0)读取4字节长度头部时,仅判断了返回值为0的对端断开场景,遗漏了两种关键情况:
  • 返回值小于0:socket发生网络错误,未正常接收数据
  • 返回值大于0但小于4:TCP是无消息边界的字节流协议,单次recv调用不保证返回你请求的全部字节数,可能只返回1/2/3个字节的头部,剩余的头部字节会被后续读取逻辑当作业务数据处理,而下次读取头部时会读到上一轮未读完的业务数据,直接导致长度解析错误。
  1. recvall函数实现错误
    函数中拿到recv的返回值recv_size后完全未做判断和使用,无论实际收到多少字节,都强制按size_to_recv的长度拷贝数据、累加已收计数:
    如果recv返回值小于size_to_recv(包括网络波动导致的短读、对端断开、系统错误等场景),就会导致实际接收的业务数据字节数不足,剩余未收的业务数据会被下一轮循环当作长度头部读取,直接出现你遇到的异常超大长度值。
    前面几次能正常接收只是因为网络状态稳定,没有触发短读场景,一旦出现拆包/网络波动就会立刻出现错位问题。

修复方案

1. 重写可靠收全数据的通用函数

// 收满size字节才返回,返回<0表示出错,返回0表示对端断开,返回>0等于size表示收全
int recvall(int fd, uint8_t *data, int size) {
    int cur_size = 0;
    while(cur_size < size) {
        int recv_size = recv(fd, data + cur_size, size - cur_size, 0);
        if (recv_size < 0) {
            // 非阻塞socket要单独处理EAGAIN/EWOULDBLOCK,这里默认是阻塞socket直接返回错误
            return -1;
        } else if (recv_size == 0) {
            return 0;
        }
        cur_size += recv_size;
    }
    return cur_size;
}

2. 修改长度头部接收逻辑,使用上面的recvall保证收满4字节

while(1)
{
        uint8_t size_message[4];
        int read_size = recvall(client_sock, size_message, 4);
        if (read_size <= 0) {
            // 对端断开或者出错,退出循环
            return 0;
        }
        printf("bytes are %d %d %d %d\n", size_message[0], size_message[1], size_message[2], size_message[3]);
        uint32_t size = toUInt32(size_message);
        printf("size is %d\n", size);
        uint8_t *data = malloc(size);
        read_size = recvall(client_sock, data, size);
        if (read_size <= 0) {
            free(data);
            return 0;
        }
        free(data);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 14:45:01