Java客户端多次接收文件触发java.io.EOFException问题求助
分析与解决方案
咱们来拆解下你遇到的问题:第一次能正常接收保存文件,第二次就触发EOFException,核心问题出在Socket流的复用逻辑和ObjectInputStream的创建时机上,同时读取字节的代码也有小瑕疵。
错误原因解析
- 流的EOF状态无法重置:第一次读完文件后,Socket的输入流已经走到了末尾(EOF)。但你的外层
while(true)会继续循环,再次创建ObjectInputStream时,它会尝试读取对象流的头部标识,可此时输入流已经没有数据了,直接触发EOFException。 - 未处理实际读取字节数:你现在的代码不管
ois.read(buf)实际读了多少字节,都直接把整个1024字节的buf写入文件,这会导致最后一次写入多余的空字节,可能损坏文件。 - 资源未正确关闭:每次循环都新建
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
相关产品推荐
相关产品推荐

