基于UDP的C Socket分段文件传输问题:MP3文件无法打开
问题分析与修复方案
看起来你遇到了UDP停止等待模式下传输二进制文件的典型问题——虽然文件长度显示正确,但实际内容是空的,根本没法打开。我仔细看了你的服务端和客户端代码,找到了两个核心问题,还有几个可以优化的小细节:
1. 二进制文件读写用了文本模式,破坏数据
你现在用"r"和"w"打开文件,这是文本模式,系统会自动处理换行符(比如把\n转换成\r\n),这种转换对TXT文件影响不大,但会直接破坏MP3这类二进制文件的字节结构,导致文件损坏。
客户端修复(文件读取)
把读取文件的代码从:
filePointer = fopen(fileName, "r");
改成:
filePointer = fopen(fileName, "rb"); // rb = 只读二进制模式
服务端修复(文件写入)
把写入文件的代码从:
filePointer=fopen(filePath, "w");
改成:
filePointer=fopen(filePath, "wb"); // wb = 写入二进制模式
2. 服务端完全没处理文件分片的数据拼接!
这是导致文件为空的直接原因:看你的服务端代码,只有处理文本(type == 1)的时候,才会把每个分片的数据拷贝到buffer的对应偏移位置,但处理文件(type == 2)的时候,完全漏掉了这一步!你malloc了buffer,但里面一直是空的,最后fwrite写入的只是一块空内存,自然文件打不开。
服务端修复(添加文件分片数据拼接逻辑)
在服务端的if (currentHeader.sequenceNumber == packetsReceived)判断块里,新增type == 2的处理分支,和文本处理逻辑类似:
if (currentHeader.type == 2) { // 计算当前分片在buffer中的偏移位置,拷贝数据 int buff_offset = fragmentSize * currentHeader.sequenceNumber; memcpy(&buffer[buff_offset], currentHeader.data, currentHeader.length); }
把这段代码放在发送ACK之前,确保每个文件分片的数据都被正确写入buffer。
3. 几个额外的优化点
- 内存泄漏问题:服务端在初始化阶段malloc了buffer,但文件处理完后没有释放,长期运行会导致内存泄漏。建议在
fclose(filePointer)之后加上free(buffer);。 - 文件名缓冲区安全:服务端的
fileNameBuffer只有15字节,如果文件名过长会导致缓冲区溢出。建议增大到至少256字节,或者用strncpy替代strcpy来避免溢出:
strncpy(fileNameBuffer, currentHeader.data, sizeof(fileNameBuffer)-1); fileNameBuffer[sizeof(fileNameBuffer)-1] = '\0'; // 手动补字符串终止符
- buffer初始化越界:服务端初始化buffer的时候,
bzero (buffer, currentHeader.length * sizeof(char));应该多初始化一个字节(因为malloc的时候加了1),改成bzero(buffer, currentHeader.length * sizeof(char) + 1);,避免后续操作越界。
修复后的核心代码片段
服务端关键修改部分
if (currentHeader.sequenceNumber == packetsReceived) { if (currentHeader.type == 0) { // 初始化包处理 buffer = malloc(currentHeader.length * sizeof(char) + 1); bzero(buffer, currentHeader.length * sizeof(char) + 1); // 修复初始化长度 if (strcmp(currentHeader.data, "NOT_FILE") != 0) { // 安全拷贝文件名 strncpy(fileNameBuffer, currentHeader.data, sizeof(fileNameBuffer)-1); fileNameBuffer[sizeof(fileNameBuffer)-1] = '\0'; } dataLength = currentHeader.length; } if (currentHeader.type == 1) { // 文本分片处理 int buff_offset = fragmentSize * currentHeader.sequenceNumber; memcpy(&buffer[buff_offset], currentHeader.data, currentHeader.length); printf("buffer: %s\n", buffer); } // 新增文件分片处理逻辑 if (currentHeader.type == 2) { int buff_offset = fragmentSize * currentHeader.sequenceNumber; memcpy(&buffer[buff_offset], currentHeader.data, currentHeader.length); } printf ("[+] ACK sent\n"); // ACK发送逻辑不变... if (packetsReceived == currentHeader.fragmentCount-1) { // 文件保存逻辑不变... fclose(filePointer); free(buffer); // 释放内存,避免泄漏 } packetsReceived++; }
客户端文件读取修改
} else if (type == 2) { filePointer = fopen(fileName, "rb"); // 二进制模式读取 if (filePointer) { fseek(filePointer, 0, SEEK_END); dataLength= ftell(filePointer); printf("DATALENGTH: %lu\n", dataLength); fseek(filePointer, 0, SEEK_SET); if(fread(fileBuffer, dataLength, 1, filePointer) <= 0) { printf("Unable to copy file into buffer or empty file.\n"); exit(1); } fclose(filePointer); } }
测试验证
修复后重新传输MP3文件:客户端用二进制模式读取数据,服务端正确拼接每个分片的数据,最后用二进制模式写入文件,这样保存的文件就会包含完整的有效数据,应该就能正常打开了。
内容的提问来源于stack exchange,提问作者Zsolti
相关产品推荐
相关产品推荐

