You need to enable JavaScript to run this app. Java Socket服务端向客户端传输文件出现死循环及文件损坏问题求解
优惠活动
大模型
产品
解决方案
定价
更多

Java Socket服务端向客户端传输文件出现死循环及文件损坏问题求解

问题根源分析
  • 缺少明确的传输边界:你的需求是保持Socket长连接支持多次双向传输,所以传输完文件不会关闭Socket,此时InputStream.read()方法永远不会返回-1,客户端的while循环会一直阻塞/死循环,无法正常结束文件写入,最终导致文件损坏。
  • 数据读取顺序错误:服务端传输文件前调用了writeUTF()发送状态字符串,客户端没有先读取该字符串,直接把状态字符串的二进制内容写入了目标文件,直接造成文件内容异常。
  • 代码逻辑缺陷:
    1. 客户端声明了BufferedOutputStream outBuffer但未实际使用,缓冲区优化无效
    2. 不必要的5秒线程休眠,既影响性能也可能引发流数据读取时序问题
    3. 服务端文件不存在的异常分支中,输出错误提示后未终止流程,后续会尝试读取null的文件对象触发空指针异常,导致传输逻辑混乱
修复方案

核心思路是在不关闭Socket的前提下,通过提前传输文件元数据(文件大小)明确传输边界,避免依赖流关闭作为传输结束标志。

服务端代码修改

try {
    String path = currentPath + "\\" + name;
    File fichierAEnvoyer = new File(path);
    // 先判断文件是否存在、是否是正常文件
    if (!fichierAEnvoyer.exists() || !fichierAEnvoyer.isFile()) {
        out.writeUTF("ERROR:Le fichier n'existe pas! Veuillez entrer son nom de nouveau!");
        return; // 异常直接终止后续流程
    }
    // 发送正常传输状态
    out.writeUTF("SUCCESS:Tentative de televersement du serveur au client.");
    // 先发送文件总大小,作为传输边界
    out.writeLong(fichierAEnvoyer.length());
    
    final short TAILLE_OCTET_MAXIMUM = 1024;
    byte[] buffer = new byte[TAILLE_OCTET_MAXIMUM];
    int count;
    OutputStream outputStream = socket.getOutputStream();
    BufferedInputStream input = new BufferedInputStream(new FileInputStream(fichierAEnvoyer));
    while ((count = input.read(buffer)) >= 0) {
        outputStream.write(buffer, 0, count);
    }
    outputStream.flush(); // 全部写完后统一刷缓冲区,不用每次循环刷
    input.close();
} catch(Exception e) {
    out.writeUTF("ERROR:Une erreur est survenue lors du televersement du serveur au client.");
    e.printStackTrace();
}

客户端代码修改

try {
    out.writeUTF(entree);
    // 移除不必要的5秒休眠
    
    // 先读取服务端返回的状态信息
    String status = in.readUTF(); // 这里的in是和服务端out对应的DataInputStream
    if (status.startsWith("ERROR")) {
        System.out.println("服务端返回错误:" + status.substring(6));
        return;
    }
    // 读取文件总大小
    long fileSize = in.readLong();
    long totalRead = 0; // 累计已读取的字节数
    FileOutputStream fos = new FileOutputStream(new File(uploadPath + "\\" + name));
    byte[] buffer = new byte[1024];
    int count;
    InputStream inputStream = socket.getInputStream();
    // 按实际文件大小控制读取结束时机
    while(totalRead < fileSize && (count = inputStream.read(buffer, 0, (int)Math.min(buffer.length, fileSize - totalRead))) >= 0){
        fos.write(buffer, 0, count);
        totalRead += count;
    }
    fos.close();
    System.out.println("Tentative de telechargement du serveur reussie!");
} catch(Exception e) {
    System.out.println("Une erreur lors de la reception du serveur est survenue...");
    e.printStackTrace();
}
长连接下文件传输的规范实现要点
  • 每次传输前先发送固定结构的元数据,包括但不限于文件大小、文件名、校验值(MD5/SHA1)等,接收方先解析元数据再接收文件内容,传输完成后用校验值验证文件完整性
  • 为Socket的输入输出操作设置超时时间,避免网络异常时出现无限阻塞
  • 使用try-with-resources语法自动管理文件流等本地资源,避免资源泄漏
  • 可以设计简单的指令协议区分不同的传输操作(上传/下载/消息通信),适配双向多轮传输的需求

内容的提问来源于stack exchange,提问作者Artour Benevolenski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 21:06:02