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

C语言Socket服务端accept成功但recv调用失败原因排查

问题根因分析

核心问题出在客户端代码的标准库流缓冲机制:

  • 客户端通过fdopen()把socket文件描述符包装成了C标准库的FILE流,默认情况下普通文件流采用全缓冲策略:只有缓冲区满、主动调用fflush()、或关闭流时,才会把缓冲区中的数据真正写入到底层的socket文件描述符。
  • 现有代码中fwrite()返回成功仅代表数据写入了用户态的标准库缓冲区,还没有发送到内核网络栈,更没有传输到服务端。写完后直接调用close(sock)关闭socket,既没有刷新标准库缓冲区,也没有关闭FILE流,缓冲区中的数据直接被丢弃,因此服务端recv()无法读取到数据,一般返回值为0,代表对端正常关闭了连接。

此外代码还存在几个非核心的问题:

  • 服务端仅在循环结束后关闭了最后一个客户端的socket,前一个客户端的socket没有及时关闭,会造成文件描述符泄漏。
  • 服务端收到数据后没有手动给readBuf补充字符串结束符,直接用printf("%s")输出可能出现乱码,recv()只会返回收到的字节数,不会自动补\0。
  • 客户端不需要设置SO_REUSEADDR、SO_REUSEPORT参数,这两个参数是给监听端口的服务端用的,对主动发起连接的客户端无意义。
修复方案

客户端修复(二选一即可,推荐第一种)

  1. 跳过标准库缓冲,直接用系统调用write()发送数据:
    替换原有fdopen、fwrite的逻辑:
    ssize_t send_len = write(sock, env_var, strlen(env_var));
    if(send_len == strlen(env_var)){
        printf("write() SUCCESS\r\n");
    } else {
        printf("FAILED on write()\r\n");
    }
    close(sock);
    
  2. 保留fwrite的话,写完后主动刷新缓冲区再关闭流:
    FILE * file = fdopen(sock, "w");
    if(fwrite (env_var ,sizeof(char), strlen(env_var), file) == strlen(env_var)){
        printf("fwrite() SUCCESS\r\n");
    } else {
        printf("FAILED on fwrite()\r\n");
    }
    fclose(file); // 关闭流会自动刷新缓冲区,同时自动关闭底层socket,不需要再单独调用close(sock)
    

服务端优化建议

  • 每次处理完单个客户端的读写后就关闭对应socket,不要等循环结束再关:
    // 每个客户端的recv逻辑结束后新增一行
    close(client_sock);
    
  • 接收数据时预留结束符位置,收到后手动补\0:
    // 最多读255字节,留1个位置放结束符
    if((read_size = recv(client_sock , readBuf , 255 , 0)) > 0){
        readBuf[read_size] = '\0'; // 手动补充字符串结束符
        printf("readBuf: %s\r\n",readBuf);
        // 后续原有逻辑保持不变
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 21:57:00