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

Java客户端多次接收文件触发java.io.EOFException问题求助

分析与解决方案

咱们来拆解下你遇到的问题:第一次能正常接收保存文件,第二次就触发EOFException,核心问题出在Socket流的复用逻辑和ObjectInputStream的创建时机上,同时读取字节的代码也有小瑕疵。

错误原因解析

  1. 流的EOF状态无法重置:第一次读完文件后,Socket的输入流已经走到了末尾(EOF)。但你的外层while(true)会继续循环,再次创建ObjectInputStream时,它会尝试读取对象流的头部标识,可此时输入流已经没有数据了,直接触发EOFException。
  2. 未处理实际读取字节数:你现在的代码不管ois.read(buf)实际读了多少字节,都直接把整个1024字节的buf写入文件,这会导致最后一次写入多余的空字节,可能损坏文件。
  3. 资源未正确关闭:每次循环都新建FileOutputStream却不关闭,会造成资源泄漏,甚至导致文件无法正常写入完成。

修复方案

根据你的使用场景,分两种情况给出修复代码:

场景1:服务器持续发送多个文件(复用Socket连接)

这种情况需要复用ObjectInputStream,不要每次循环都重建,同时正确处理读取长度和资源关闭:

try { 
    Socket socket = new Socket("localhost", 9999); 
    // 只创建一次ObjectInputStream,复用该流
    ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); 
    while(true) { 
        // 建议先接收文件名/文件长度,避免重复覆盖同一个文件
        String fileName = (String) ois.readObject(); 
        long fileLength = ois.readLong();
        
        byte[] buf = new byte[1024]; 
        FileOutputStream fout = new FileOutputStream("c:/hyebin/hyebin/" + fileName); 
        long received = 0;
        int len;
        // 按实际读取长度写入,同时根据文件长度控制读取,避免读到EOF阻塞
        while(received < fileLength && (len = ois.read(buf, 0, (int)Math.min(buf.length, fileLength - received))) != -1){ 
            fout.write(buf, 0, len); 
            received += len;
        } 
        fout.close(); // 关闭文件输出流
        System.out.println("파일 " + fileName + " 수신 및 저장 성공"); 
    } 
} catch (Exception ex) { 
    ex.printStackTrace(); 
}

注意:这种场景下服务器端需要配合,每次发送文件前先传文件名和长度,且不要关闭Socket连接,持续发送数据。

场景2:服务器发送一次文件就关闭连接(每次接收重建连接)

如果服务器是处理一个文件请求就关闭连接,客户端应该每次循环都重新创建Socket:

while(true) { 
    try { 
        Socket socket = new Socket("localhost", 9999); 
        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); 
        byte[] buf = new byte[1024]; 
        FileOutputStream fout = new FileOutputStream("c:/hyebin/hyebin/excercise.jpg"); 
        int len;
        while((len = ois.read(buf, 0, buf.length)) != -1){ 
            fout.write(buf, 0, len); 
        } 
        // 用完即关闭资源
        fout.close();
        ois.close();
        socket.close();
        System.out.println("파일 수신 및 저장 성공"); 
    } catch (Exception ex) { 
        ex.printStackTrace(); 
        // 可根据需求添加重试或退出逻辑
        break;
    }
}

这种方式每次循环都重建连接,避免了复用已到EOF的流的问题。

额外提示

  • 推荐使用Java 7+的try-with-resources语法自动关闭流,避免手动关闭遗漏:
    try (FileOutputStream fout = new FileOutputStream("xxx")) {
        // 写入逻辑
    }
    
  • 如果服务器发送的是纯字节文件数据,其实没必要用ObjectInputStream,直接用socket.getInputStream()即可,ObjectInputStream是用来序列化Java对象的,用在这里反而容易因为流头校验出问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:47:58