C语言Socket接收文件后写入磁盘不完整的问题排查
问题分析与修复方案
核心问题(和缓冲区大小无关)
你遇到的写入不完整问题,主要是代码里的几个逻辑错误导致的,和缓冲区大小关系不大:
- 服务器端
read的长度错误:buff是指针,sizeof(buff)得到的是指针本身的字节数(32位系统是4,64位是8),不是你malloc的1024字节。这导致每次只读取了几个字节,大量数据没被处理,写入自然不完整。 - 按字符串写入的隐患:
fprintf/fputs是处理字符串的,遇到\0就会停止写入。如果文件包含二进制数据(比如图片、exe),或者read读取的内容没有以\0结尾,就会直接截断数据。 - 未处理
read的边界返回值:read返回0表示客户端已经关闭连接,此时不退出会无限循环;read返回-1表示读取出错,需要及时终止流程。
- 文件打开模式错误:用
"w"是文本模式,处理二进制文件时会自动转换换行符(比如Windows下把\n转成\r\n),导致文件内容损坏,应该用二进制模式"wb"。 - 客户端
send的长度错误:sizeof(data)是1024,但fgets实际读取的字节数远小于这个值(比如一行只有几十字节),剩下的都是\0,不仅浪费带宽,还会让服务器端处理多余的空数据。
修正后的服务器代码片段
#define MAX 1024 void whileRead(int *sockfd) { // 二进制模式打开文件,避免文本模式的换行符转换 FILE *output = fopen("copy", "wb"); if (output == NULL) { perror("fopen failed"); pthread_exit(NULL); } int n; char *buff = malloc(sizeof(char) * MAX); if (buff == NULL) { perror("malloc failed"); fclose(output); pthread_exit(NULL); } while(1) { bzero(buff, MAX); // 用MAX初始化缓冲区,不是sizeof(buff) n = read(*sockfd, buff, MAX); if (n > 0) { // 按实际读取的字节数打印和写入,避免字符串截断问题 fwrite(buff, 1, n, stdout); fwrite(buff, 1, n, output); // 确保读取到足够字节后再判断exit指令 if (n >= 4 && strncmp(buff, "exit", 4) == 0) { printf("Client Exit...\n"); break; } } else if (n == 0) { // 客户端主动关闭连接 printf("Client disconnected\n"); break; } else { // 读取出错 perror("read failed"); break; } } free(buff); fclose(output); pthread_exit(NULL); }
修正后的客户端代码片段
#define SIZE 1024 struct arg_struct { SOCKET sock; FILE* input; }; void *whileWrite(void* arg) { int n; char data[SIZE] = {0}; struct arg_struct *args = (struct arg_struct*)arg; // 按实际读取的长度发送,避免发送多余的空字节 while(fgets(data, SIZE, args->input) != NULL) { size_t len = strlen(data); // 循环发送,确保所有数据都被发送(send可能只发送部分数据) ssize_t sent = 0; while (sent < len) { ssize_t ret = send(args->sock, data + sent, len - sent, 0); if (ret < 0) { perror("[-]Error in sending file."); exit(1); } sent += ret; } bzero(data, SIZE); } // 发送exit标记,告诉服务器传输结束 const char* exit_cmd = "exit"; send(args->sock, exit_cmd, strlen(exit_cmd), 0); return NULL; }
额外说明
如果传输的是二进制文件,建议客户端用fread代替fgets,因为fgets会在遇到\n或达到缓冲区大小时停止,无法正确读取二进制数据中的特殊字符。
内容的提问来源于stack exchange,提问作者Amusing6028
相关产品推荐
相关产品推荐

