基于C语言的TCP Socket图片传输问题:上传下载陷死循环
C语言TCP Socket文件传输死循环问题排查与修复
核心问题分析
运算符优先级错误:你的while循环里的赋值与比较语句优先级搞反了,比如bytesread = fread(buff, 1, sizeof(buff), file_fd) > 0,>的优先级高于=,实际执行的是bytesread = (fread(...) > 0),这会把布尔值(0或1)赋值给bytesread,导致后续send的字节数错误,同时循环条件可能一直为真(只要fread返回非0,就赋值1,循环继续),陷入死循环。
TCP流式协议无边界,命令/文件名传输无分隔:客户端连续send命令和文件名,服务器的recv可能一次性读到拼接的内容(比如"UPLOADfilename"),导致命令判断错误,后续逻辑混乱。
传输结束无明确标识:文件传输完成后,没有通知对方结束,比如客户端上传完文件后,服务器的recv会一直阻塞等待数据,陷入死循环;下载时同理,客户端的recv会一直等服务器发数据。
send参数错误:download函数里的send(sock, "DOWNLOAD", strlen("DOWNLOAD"), 8),最后一个flags参数用了8,这是无效值,应该用0。
缺乏错误处理:fopen、send、recv等操作失败时没有做处理,比如文件打不开直接进入循环,会导致后续操作异常。
修复后的代码
client.c 修改版
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #define True 1 int upload(int sock, const char* file_name){ FILE *file_fd; char buff[1024]; int bytesread, bytessent; // 发送命令,用换行作为分隔符 send(sock, "UPLOAD\n", strlen("UPLOAD\n"), 0); // 发送文件名+换行分隔 send(sock, file_name, strlen(file_name), 0); send(sock, "\n", 1, 0); file_fd = fopen(file_name, "rb"); if (!file_fd) { perror("fopen upload file failed"); return 0; } // 修正运算符优先级,添加括号确保先赋值再比较 while((bytesread = fread(buff, 1, sizeof(buff), file_fd)) > 0) { bytessent = send(sock, buff, bytesread, 0); if (bytessent < 0) { perror("send failed"); fclose(file_fd); return 0; } } fclose(file_fd); // 关闭socket写端,通知服务器传输结束 shutdown(sock, SHUT_WR); // 等待服务器确认后再删除文件 recv(sock, buff, sizeof(buff), 0); remove(file_name); return True; } int download(int sock, const char* file_name){ FILE *file_fd; char buff[1024]; int bytesgot; // 修正send的flags参数为0 send(sock, "DOWNLOAD\n", strlen("DOWNLOAD\n"), 0); send(sock, file_name, strlen(file_name), 0); send(sock, "\n", 1, 0); file_fd = fopen(file_name, "wb"); if (!file_fd) { perror("fopen download file failed"); return 0; } // 修正运算符优先级 while((bytesgot = recv(sock, buff, sizeof(buff), 0)) > 0) { fwrite(buff, 1, bytesgot, file_fd); } if (bytesgot < 0) { perror("recv failed"); fclose(file_fd); remove(file_name); return 0; } fclose(file_fd); // 发送确认给服务器 send(sock, "OK", 2, 0); return True; }
server.c 修改版
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #define True 1 // 辅助函数:读取一行数据,用于分隔命令和文件名 ssize_t read_line(int sock, char *buf, size_t max_len) { ssize_t n; char c; size_t i = 0; while (i < max_len - 1) { n = recv(sock, &c, 1, 0); if (n <= 0) { return n; } if (c == '\n') { break; } buf[i++] = c; } buf[i] = '\0'; return i; } int main() { int clnt_sock; // 假设已完成客户端连接 char buffer1[1024], filename[256], buffer2[1024]; int n, bytesread; FILE *file_fd; while(True){ // 读取完整命令行 n = read_line(clnt_sock, buffer1, sizeof(buffer1)); if (n <= 0) { // 客户端断开连接 break; } // 处理上传请求 if(strncmp(buffer1, "UPLOAD", 6) == 0) { // 读取完整文件名 n = read_line(clnt_sock, filename, sizeof(filename)); if (n <= 0) { break; } file_fd = fopen(filename, "wb"); if (!file_fd) { perror("fopen upload file failed"); send(clnt_sock, "FAIL", 4, 0); continue; } // 接收文件内容,recv返回0时退出循环 while((bytesread = recv(clnt_sock, buffer2, sizeof(buffer2), 0)) > 0) { fwrite(buffer2, 1, bytesread, file_fd); } if (bytesread < 0) { perror("recv failed"); fclose(file_fd); remove(filename); send(clnt_sock, "FAIL", 4, 0); continue; } fclose(file_fd); send(clnt_sock, "OK", 2, 0); } // 处理下载请求 else if(strncmp(buffer1, "DOWNLOAD", 8) == 0) { n = read_line(clnt_sock, filename, sizeof(filename)); if (n <= 0) { break; } file_fd = fopen(filename, "rb"); if (!file_fd) { perror("fopen download file failed"); shutdown(clnt_sock, SHUT_WR); continue; } while((bytesread = fread(buffer2, 1, sizeof(buffer2), file_fd)) > 0) { if (send(clnt_sock, buffer2, bytesread, 0) < 0) { perror("send failed"); fclose(file_fd); break; } } fclose(file_fd); // 关闭写端通知客户端传输结束 shutdown(clnt_sock, SHUT_WR); // 等待客户端确认 recv(clnt_sock, buffer2, sizeof(buffer2), 0); remove(filename); } } return 0; }
关键修复点说明
- 修正运算符优先级:所有
变量 = 函数调用 > 0的语句添加括号,确保先完成赋值再进行比较,避免逻辑错误。 - 添加传输边界处理:用换行符作为命令和文件名的分隔符,通过
read_line函数读取完整的命令和文件名,解决TCP流式传输的内容拼接问题。 - 明确传输结束标识:文件传输完成后调用
shutdown(sock, SHUT_WR)关闭socket写端,对方recv会返回0,从而正常退出循环,避免死循环。 - 修正send参数:将无效的flags参数8改为0,符合socket API规范。
- 完善错误处理:对fopen、send、recv等操作添加错误检查,及时处理异常情况,避免逻辑崩溃。
内容的提问来源于stack exchange,提问作者JUSTIN JANG
相关产品推荐
相关产品推荐

