Java 检测关闭的流

Posted

技术标签:

【中文标题】Java 检测关闭的流【英文标题】:Java Detect Closed Stream 【发布时间】:2011-08-02 07:57:08 【问题描述】:

我有一个由 OutputStream 和 InputStream 组成的通用套接字实现。

完成一些工作后,我将关闭 OutputStream。

完成后,我的 InputStream 的 read() 方法会在无限时间内返回 -1,而不是像我预期的那样抛出异常。

我现在不确定最安全的路线,所以我有几个问题:

我可以安全地假设 -1 只是 流关闭时返回? 有没有办法重新创建 IO 发生的异常时 连接被强行断开? 我是否应该发送一个数据包来告诉我的 InputStream 它应该关闭而不是前两种方法?

谢谢!

【问题讨论】:

如果您的代码编写正确,您将永远不会遇到异常。如果您依赖于为控制流引发异常,则说明您的代码存在问题。 【参考方案1】:

我可以安全地假设 -1 仅在流关闭时返回吗?

是的。

你不应该假设这样的事情。您应该阅读 javadoc 并根据 API 的指定行为方式进行实施。特别是如果您希望您的代码健壮(或者如您所说的“安全”)。

话虽如此,在这种情况下,这 或多或少是 javadoc 所说的。 (有人可能会争辩说,EOF 和“流已关闭”不一定意味着同一件事......并且通过调用 InputStream.close()Socket.close() locally 来关闭流会有不同的效果。但是,这些都与您的用例没有直接关系。)

有没有办法重新创建连接被强行断开时发生的IO异常?

没有。首先,通常不会首先抛出异常,因此通常没有什么可以“重新创建”。其次,原始异常中的信息(如果有的话)已经消失了。

我是否应该发送一个数据包来告诉我的 InputStream 它应该关闭而不是前两种方法?

没有。最好的方法是测试read 调用的结果。无论如何,您都需要对其进行测试,因为您不能假设 read(byte[]) 方法(或其他方法)将返回您实际请求的字节数。

我想抛出一个特定于应用程序的异常是可以的在某些情况下

但请记住一般原则,异常不应用于正常的流控制。


其他答案之一建议创建一个代理 InputStream 引发一些异常而不是返回 -1。

IMO,这是个坏主意。您最终会得到一个声称是InputStream 的代理类,但它违反了read 方法的约定。如果将代理传递给期望正确实现的InputStream 的东西,这可能会导致麻烦。

其次,InputStreamabstract class 而不是interface,所以Java 的动态代理机制不起作用。 (例如,newProxyInstance 方法需要一个接口列表,而不是类。)

【讨论】:

【参考方案2】:

有没有办法重新创建连接被强行断开时发生的IO异常?

我会回答这个问题。 InputStream 只是一个接口。如果您真的希望实现在 EOF 上引发异常,请提供您自己的小型包装器,覆盖 read() 并在 -1 结果上引发异常。

最简单(最少编码)的方法是使用动态代理:

InputStream pxy = (InputStream) java.lang.reflect.Proxy.newProxyInstance(
    obj.getClass().getClassLoader(),
    new Class[] InputStream.class ,
    new ThrowOnEOFProxy(obj));

ThrowOnEOFProxy 将检查方法名称,调用它,如果结果为 -1,则抛出 IOException("EOF")。

【讨论】:

InputStream 不是接口,所以Proxy.newProxyInstance 在这种情况下不起作用;看我的回答。 糟糕 :) 用了 10 年了,肯定是这样! :D LOL 不会删除答案以提醒自己仔细检查我在写的内容 %-)【参考方案3】:

此外,关闭套接字中的输出流也会关闭套接字本身。

这是 JavaDoc for Socket 所说的:

公共输出流 getOutputStream() 抛出 IOException

Returns an output stream for this socket.

If this socket has an associated channel then the resulting output

stream 委托其所有操作 到频道。如果频道在 非阻塞模式然后输出 流的写操作会抛出 一个 IllegalBlockingModeException。

Closing the returned OutputStream will close the associated socket.

Returns:
    an output stream for writing bytes to this socket. 
Throws:
    IOException - if an I/O error occurs when creating the output stream

或者如果套接字没有连接。

不确定这是您真正想要做的。

【讨论】:

【参考方案4】:

根据InputStream javadoc,read() 返回:

下一个数据字节,如果到达流的末尾,则为 -1。

因此,您可以放心地假设,最好使用 API 中指定的内容,而不是尝试重新创建异常,因为抛出的异常可能取决于实现。

【讨论】:

【参考方案5】:

-1 是流结束时的预期行为。见InputStream.read()

从输入流中读取数据的下一个字节。值字节作为 int 返回,范围为 0 到 255。如果由于到达流的末尾而没有可用的字节,则返回值 -1。此方法阻塞,直到输入数据可用,检测到流结束,或抛出异常。

当然,对于意外事件,您仍然应该关注IOException

【讨论】:

以上是关于Java 检测关闭的流的主要内容,如果未能解决你的问题,请参考以下文章

我应该关闭用 java.nio.file.Files.newInputStream 创建的流吗?

告别复杂的流关闭

java IO包装流如何关闭

Java IO流关闭问题的深入研究

从 getInputStream 正确关闭 Java Process InputStream

MemoryStream,无法访问已关闭的流