套接字关闭问题 – 最后一部分数据丢失
我有一个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的情况,然后你应该拥有一切。