C语言SOCKET接收文件函数无限循环、输出文件乱码问题排查
问题现象
- 实现了入参为SOCKET句柄、输出文件名的
recv_file函数,用于接收网络数据写入指定文件。编译运行后程序无法正常退出,输出的file.txt存在无法识别的乱码。其余代码均测试正常,可确定问题出在该函数内部。 - 运行时控制台仅输出一行
recv count : 65,do-while循环后的printf语句始终没有打印。尝试调用closesocket(soc);WSACleanup();未解决问题,更换netcat、python socketserver作为服务端测试,异常完全复现。
问题代码
int recv_file(SOCKET soc, const char * fname) { FILE * ptr; char buffer[MAX]; unsigned long long int x = 0; if (fopen_s(&ptr, fname , "wb") != 0) { printf("Failed to receive file \n"); return 1; } do { c = recv(soc, buffer, MAX, 0); printf("recv count : %d\n",c); // here was -> fwrite(buffer, 1, sizeof(buffer), ptr); fwrite(buffer, 1, c, ptr); } while ( c > 0); printf("Total received : %lld\n", x); fclose(ptr); return 0; }
错误原因
代码存在4个明确问题:
- 变量未定义:循环内使用的
c变量没有提前声明,属于未定义行为,正常编译应抛出警告/错误,会导致运行时逻辑不可预期。 - 程序阻塞根源:TCP的
recv是阻塞调用,仅在三种情况下返回:对端主动关闭连接、套接字出错、成功接收到数据。第一次调用recv拿到65字节数据后,循环会再次触发recv调用,此时如果服务端既没有关闭连接,也没有发送新数据,recv会一直挂起等待,永远不会返回,所以循环后的代码不会执行,程序卡死无法退出。 - 文件乱码根源:注释掉的旧写法
fwrite(buffer, 1, sizeof(buffer), ptr);逻辑错误——sizeof(buffer)是整个缓冲区的固定长度,而recv实际只返回c字节的有效数据,缓冲区剩余部分是未初始化的随机内存值,直接把整个缓冲区写入文件就会产生乱码。修改后的fwrite(buffer, 1, c, ptr);写法本身正确,但没有提前判断c的合法性,当c为0或负数时写入会触发新的异常。 - 逻辑遗漏:定义了用于统计总接收字节数的变量
x,但全程没有做累加操作,最后打印的x永远是初始值0。
修复后代码
int recv_file(SOCKET soc, const char * fname) { FILE * ptr; char buffer[MAX]; int c = 0; // 提前定义recv返回值变量 unsigned long long int x = 0; if (fopen_s(&ptr, fname , "wb") != 0) { printf("Failed to open file for write\n"); return 1; } while (1) { c = recv(soc, buffer, MAX, 0); if (c > 0) { printf("recv count : %d\n",c); fwrite(buffer, 1, c, ptr); x += c; // 累加总接收字节数 } else if (c == 0) { // 对端正常关闭连接,退出接收循环 printf("Connection closed by remote peer\n"); break; } else { // recv调用出错,打印错误码后退出 printf("Recv failed, error code: %d\n", WSAGetLastError()); break; } } printf("Total received : %lld\n", x); fclose(ptr); return 0; }
额外注意
如果使用长连接传输文件,不能等对端关闭连接才结束接收,需要在应用层协议提前约定文件总长度,接收到的字节数达到约定长度后就主动停止接收,否则长连接不主动断开的情况下,recv会一直阻塞。
内容的提问来源于stack exchange,提问作者packet_sniffer
相关产品推荐
相关产品推荐

