代码之家  ›  专栏  ›  技术社区  ›  Steffen Heil

为什么ioExceptions出现在readableBytechannel.read()中?

  •  2
  • Steffen Heil  · 技术社区  · 15 年前

    规范 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. 假设读操作引发的任何异常都是关于套接字被关闭的,这是安全的吗?
    3. 所有这些都一样吗 write() ?

    顺便问一下:如果我打电话 SocketChannel.close() 我要打电话吗 SocketChannel.socket().close() 或者这是早先的暗示?

    谢谢,史蒂芬

    2 回复  |  直到 15 年前
        1
  •  2
  •   Alexander Pogrebnyak    15 年前

    很有趣,但今天有人已经发布了一个链接 Fallacies of Distributed Computing .

    就你而言,正如一位优秀的德英翻译告诉我的那样 An existing connection was forcibly closed by remote host .

    当您处理I/O,特别是套接字I/O时,必须准备好任何IOException都会向您抛出。

    其中一些,比如 ClosedByInterruptException 你可以聪明地处理。其他人,你可以

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

    在任何情况下,一旦你得到IOException,你可能就不能做太多的事情来与那个频道通信。因此,您可以关闭连接,稍后重试。

    顺便说一句, read 只会返回-1给你,如果你 成功 已到达流的结尾。在您的情况下,我确信连接在中游关闭。

    编辑以回复操作注释

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

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

    的文档 CloseDBYInterruptException状态: “已检查的异常 当另一个线程中断时线程 当它被I/O阻塞时 在一个通道上操作。 如果是的话,它会不会堵塞? 非阻塞IO?

    如果你看一下 ReadableByteChannel.read 您将看到该方法声明为只抛出 IOException . 然后在JavaDoc中,它提示了具体的IOExceptions类型,并取消了该接口的标准java.nio实现可能引发的条件。这是一个合作编程的例子。接口的声明器告诉实现者应该如何实现方法,以及在什么条件下应该抛出什么异常。

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

    在任何情况下,我认为您对read方法中声明的不同异常应该如何反应感到困惑。 首先,不是所有读方法规范中的异常都可能被抛出。案例分析 ClodesByInterruptException 如果您使用的是非阻塞I/O,则很可能不会被抛出。另一方面,一些实现者可能会选择在收到中断时关闭通道,并在尝试读取时抛出此异常,即使您处于非阻塞I/O中也如此。但您确实不应该担心因为:

    1. 你可以控制电流 线程是否中断
    2. 这只是另一种 IOException,默认情况下应该 被认为是致命的

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

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

    因为框架是由人编写的,它们也有缺陷、缺陷等,所以在抛出IOException时可能会出现这种情况,但底层通道仍然正常。不幸的是,只有在真实的生产环境中,血液和内脏才能发现这些情况。下面是一个例子,其中一个套接字引发的IOException可以完全忽略: http://bugs.sun.com/view_bug.do?bug_id=4516760 .

    所以,从为所有IOException编写通用处理程序开始,并将它们都视为致命的,对于您在生产中发现的特定条件,可以放宽它。

        2
  •  0
  •   Dancrumb    15 年前

    根据文件 here :

    线程接收的已检查异常 当另一个线程中断它时 当它在I/O中被阻塞时 通道上的操作。在此之前 引发异常通道将 已经关闭和中断 以前阻止的状态 将设置线程。

    这表明拥有 ReadableByteChannel 正在被应用程序中的另一个线程中断…是这样吗?

    如果另一个线程正在关闭通道,我希望您看到 AsynchronousCloseException .

    我不明白为什么你不能抓住 ClosedByInterruptException 显式并允许在堆栈更高的位置处理任何其他异常:

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

    至于 write() ,没有 写入() 方法 可读取的字节通道 但是, WritableByteChannel , 有一个 写入() 方法,它可以抛出相同的异常。