套接字关闭问题 – 最后一部分数据丢失

我有一个Java程序接受连接,接收HTTP请求并发送HTTP回复和一些存储在文件中的数据(这是缓存代理的一部分)。 删除所有不相关的内容,我的代码如下所示:

FileInputStream fileInputStream = new FileInputStream(file); OutputStream outputStream = socket.getOutputStream(); byte[] buf = new byte[BUFFER_SIZE]; int len = 0; while ((len = fileInputStream.read(buf)) > 0) { outputStream.write(buf, 0, len); } outputStream.flush(); socket.close(); 

此代码在每个连接的客户端的特定线程中执行。

当我处理小文件(.htm,.gif,.swf等)时,一切正常(但是,我没有看到浏览器中的任何错误)。 但是当我下载大文件(.iso),特别是同时下载几个文件时,当系统处于负载状态时,有时我会得到非常奇怪的行为。 浏览器下载99.99%的文件,当下载的字节少于BUFFER_SIZE时,下载会停止几秒钟,然后浏览器会说错误已经发生。 我无法理解会发生什么,因为所有数据都已成功读取,甚至所有数据都成功写入outputStream。 正如你所看到的,我甚至做了flush(),但它没有结果。

有谁能解释一下会发生什么?

编辑
将项目上传到filehosting.org。
下载源文件 。 有zip源存档,包含源代码,Build.xml和Readme.txt。 使用ant构建解决方案。 在ClientManager.java中发生了所描述的问题,您可以在那里找到评论。

基于JDK 1.6代码库的快速浏览:

  • socket.getOutputStream()返回一个SocketOutputStream实例
  • flush()确实对SocketOutputStream实例没有影响
  • SocketOutputStream实例上的write()似乎没有缓冲Java代码中的任何内容
  • shutdownOutput()应确保在关闭套接字的输出端之前写入任何未完成的数据。 至少,评论说。

但是,一些Socket等实现是本机方法,我没有深入研究。

根据我的判断,“正确”序列将是:

 socket.shutdownOutput(); socket.close(); 

但是,你说你已经尝试过了。 另一端的应用程序是否可能提前关闭TCP / IP连接?

另一个想法:你尝试过setSoLinger(true, 60000) ,但操作系统允许的60000秒可能更长。 尝试setSoLinger(true, 60) ,并在打开输出流之前尝试执行此操作。

不知道为什么会发生这种情况,我发现你的代码没有问题。 您是否尝试使用BufferedInputStream来封装FileInputStream

我认为最可能的原因是您关闭连接而不等待数据完全传输。

你可以让Java等一会儿给你,

 socket.setOption(SocketOptions.SO_LINGER, new Integer(60)); 

我习惯先发送长度,然后等到我收到数据,但是如果你不能这样做,这个博客应该会有所帮助:

http://vadmyst.blogspot.com/2008/04/proper-way-to-close-tcp-socket.html

这适用于.NET,但基本逻辑应该相同。

一旦发送了数据,就需要让发送方关闭它们的连接,然后执行shutdownOutput并让另一方继续读取,直到出现无法读取任何内容或抛出exception的情况,然后你应该拥有一切。