You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:49:50