【问题标题】:Why do IOExceptions occur in ReadableByteChannel.read()ReadableByteChannel.read()为什么会出现IOExceptions
【发布时间】:2011-01-27 20:23:56
【问题描述】:

ReadableByteChannel.read() 的规范将-1 显示为流结束的结果值。此外,如果线程被中断,它会指定ClosedByInterruptException作为可能的结果。

现在我想这就是全部了——而且大部分时间都是这样。但是,有时我会得到以下信息:

java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
 at sun.nio.ch.SocketDispatcher.read0(Native Method)
 at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:25)
 at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:233)
 at sun.nio.ch.IOUtil.read(IOUtil.java:206)
 at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:236)
 at ...

我不明白为什么在这种情况下我没有得到-1。这也不是一个干净的例外,因为如果不捕获任何可能的IOException,我就无法捕获它。

所以这是我的问题:

  1. 为什么首先抛出这个异常?
  2. 是否可以安全地假设 read 引发的任何异常都与套接字关闭有关?
  3. write() 的所有这些都一样吗?

顺便说一句:如果我打电话给SocketChannel.close(),我是否也必须打电话给SocketChannel.socket().close(),还是前面的暗示暗示了这一点?

谢谢,史蒂芬

【问题讨论】:

  • 从谷歌翻译的异常翻译:“一个现有的连接被远程主机强行关闭”
  • @Steffen:我已经尝试回答您的一些问题。希望它会有所帮助。

标签: java sockets exception nio


【解决方案1】:

很有趣,但是今天有人已经发布了指向 Fallacies of Distributed Computing 的链接。

在你的情况下,正如一位优秀的德英翻译告诉我 An existing connection was forcibly closed by remote host

当您处理 I/O,特别是 Socket I/O 时,您必须做好准备,以免向您抛出任何 IOException。

其中一些,例如ClosedByInterruptException,您可能会很聪明地处理。其他的,你可以

  1. 给你的方法添加throws声明,让调用者处理IOExceptions
  2. 将 IOExceptions 包装到特定于您的子系统的已检查异常中
  3. 将 IOExceptions 包装到特定于您的子系统或不特定于您的子系统的 RuntimeException 中
  4. 记录 IOException 并继续。

无论如何,一旦您得到 IOException,您可能无法与该通道进行太多通信。因此,您可以关闭连接并稍后重试。

顺便说一句,read 只会在您成功到达流尾时返回 -1。在你的情况下,我敢肯定,连接在中途关闭。

编辑回复 OP cmets

我可以阻止一个频道 可中断?

不,这是您正在使用的特定频道的属性。如果它实现了InterruptibleChannel,那么它是可中断的,并且将在收到线程中断时关闭。 java.nio.channels.SocketChannel 是可中断的

文档 ClosedByInterruptException 状态: “一个已检查的异常收到 当另一个线程中断时线程 它在 I/O 中被阻塞时 对通道进行操作。” 仍然如何 是否会阻塞,如果是的话 非阻塞 IO?

如果您查看ReadableByteChannel.read 的规范,您将看到该方法被声明为仅抛出IOException。然后在 JavaDoc 中,它提示了该接口的标准 java.nio 实现可能会抛出什么样的特定 IOExceptions 和 unre 什么条件。这是合作编程的例子之一。接口的声明者告诉实现者应该如何实现方法,在什么条件下应该抛出什么异常。

例如,如果我在我的奇异频道中实现 read 方法,我可能会选择完全忽略规范并且不在声明中抛出任何异常。不过,我班级的用户在遇到意外行为时可能会付出沉重的代价,并且可能会放弃我的实现以支持更健壮的实现。

无论如何,我认为您对如何应对 read 方法中声明的不同异常感到困惑。 首先,并非读取方法规范中的每个异常都可能被抛出。如果您使用的是非阻塞 I/O,ClodesByInterruptException 很可能不会被抛出。另一方面,一些实现者可能会选择在收到中断时关闭通道,并在您尝试读取时抛出此异常,即使您处于非阻塞 I/O 中也是如此。但你真的不应该担心这个决定,因为:

  1. 您可能控制当前是否 线程是否中断
  2. 这只是另一种 IOException,默认情况下应该 被视为致命的

是的,示例 IOEception 是 致命的,但我的问题是:他们是 全部?

请记住,您的框架应该正常运行,或者在抛出任何类型的异常时正常关闭。例如,您的代码或库代码在任何时候都可能引发未经检查的(运行时)异常。只有对您的环境进行测试和更深入的了解才能告诉您哪些异常是真正致命的,哪些可以安全处理。

因为框架是人写的,所以也会有bug、不足等。所以可能会有抛出IOException的情况,但是底层的channel还是可以的。不幸的是,这些条件只能在真实的生产环境中通过血液和内脏找到。这是可以完全忽略套接字抛出的 IOException 的示例之一:http://bugs.sun.com/view_bug.do?bug_id=4516760

因此,首先为所有 IOExceptions 编写通用处理程序并将它们全部视为致命的,然后针对您在生产中发现的特定条件放宽它。

【讨论】:

  • 我知道这些谬误,我尊重它们。我不明白的是: SUCCESSFULLY 到达流尾和“中游”关闭的流之间有什么区别。请注意,我正在读入一个字节缓冲区,我并没有尝试反序列化任何东西。所以在物理上,当接收到 TCP FIN 时,流中根本没有更多的字节。就这样?
  • 还有一条评论:你确定,在我得到任何IOException 之后,我不能再对套接字做任何事情了吗?如果这是安全的,我将简单地捕获IOExceptions 并像现在处理ClosedByInterruptException 一样处理它们:我认为这个套接字已关闭。我仍然不明白以下两点:为什么我没有得到 -1 以及为什么会出现 ClosedByInterruptException ?我的套接字都没有阻塞,ClosedByInterruptException 的规范似乎暗示了这一点。
  • @Steffen:如果你使用的是非阻塞 I/O,你确定你已经轮询了一个选择器,有什么东西可以从通道中读取吗?如果 selector 认为没有什么可读取的,那么你在尝试这样做的时候肯定会得到一个异常。此外ClosedByInterruptException 并不意味着您的 I/O 是非阻塞的。这仅表示您的频道是可中断的。关于 IOExceptions,一般而言,java.io 或 java.nio 抛出的异常或多或少是致命的,因此将您的连接视为 dead 是一个不错的选择。你得到的那个,看起来绝对是致命的。
  • 是的,我很确定,只有当 OP_READ 在 readyOps() 中时,我的 read() 才能被调用。在 read() 周围仍然有一个循环,如果没有要读取的内容但返回 0(按规范),它不应该抛出异常。 ClosedByInterruptException 状态的文档:“当另一个线程在通道上的 I/O 操作中被阻塞时,另一个线程中断它时,线程接收到的已检查异常。”如果它是非阻塞 IO,它怎么会阻塞呢?我可以防止频道被中断吗?是的,IOEception 的例子是致命的,但我的问题是:它们都是吗?
【解决方案2】:

根据文档here

线程接收到的已检查异常 当另一个线程中断它时 当它在 I/O 中被阻塞时 对通道进行操作。在这之前 抛出异常通道将 已关闭且中断 先前被阻止的状态 线程将被设置。

这表明拥有ReadableByteChannel 的线程正在被您的应用程序中的另一个线程中断...是这种情况吗?

如果另一个线程正在关闭频道,我希望你会看到AsynchronousCloseException

我看不出为什么您不能明确地捕获 ClosedByInterruptException 并允许在堆栈的更高层处理任何其他异常:

try
{
    rbc.read(dst);
} 
catch ( ClosedByInterruptException cbie)
{
    /* handler */
}

对于write()ReadableByteChannel 没有write() 方法,但是,WritableByteChannel确实 有一个write() 方法,它可以抛出相同的异常。

【讨论】:

  • 我可以捕捉到ClosedByInterruptException,但我没有得到那个,而只是一个IOException(参见上面的堆栈跟踪)。同样在我的情况下,远程关闭了连接,线程没有被中断。 (当然,我在WriteableByteChannel 中提到了write()。我希望这是可以预料的。
猜你喜欢
  • 2011-04-12
  • 2020-04-29
  • 2021-07-29
  • 2012-03-26
  • 2012-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多