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参数,这两个参数是给监听端口的服务端用的,对主动发起连接的客户端无意义。
修复方案
客户端修复(二选一即可,推荐第一种)
- 跳过标准库缓冲,直接用系统调用
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); - 保留
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
相关产品推荐
相关产品推荐

