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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 06:01:16