views:

201

answers:

2

Hi

The specification of ReadableByteChannel.read() shows -1 as result value for end-of-stream. Moreover it specifies ClosedByInterruptExceptionas possible result if the thread is interrupted.

Now I thought that would be all - and it is most of the time. However, now and then I get the following:

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 ...

I do not unterstand why I don't get -1 in this case. Also this is not a clean exception, as I cannot catch it without catching any possible IOException.

So here are my questions:

  1. Why is this exception thrown in the first place?
  2. Is it safe to assume that ANY exception thrown by read are about the socket being closed?
  3. Is all this the same for write()?

And by the way: If I call SocketChannel.close() do I have to call SocketChannel.socket().close() as well or is this implied by the earlier?

Thanks, Steffen

A: 

Per the documentation here:

Checked exception received by a thread when another thread interrupts it while it is blocked in an I/O operation upon a channel. Before this exception is thrown the channel will have been closed and the interrupt status of the previously-blocked thread will have been set.

This suggests that the thread that owns the ReadableByteChannel is being interrupted by another thread in your application... is this the case?

If another thread was closing the channel, I would expect you would see a AsynchronousCloseException.

I don't see a reason why you couldn't catch the ClosedByInterruptException explicitly and allow any other exceptions to be handled higher up the stack:

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

As for write(), there is no write() method for ReadableByteChannel, however, the WritableByteChannel, does have a write() method and it can throw the same exceptions.

Dancrumb
I CAN catch `ClosedByInterruptException` but I am not getting that one but just an `IOException` (see stacktrace above). Also in my case, the connection is close by the remote, the thread is NOT interrupted. (Surely I ment `write()` in `WriteableByteChannel`. I expected that to be expectable.
Steffen Heil
+1  A: 

Funny, but somebody today already posted a link to Fallacies of Distributed Computing.

In your case, as a fine German to English translator tells me An existing connection was forcibly closed by remote host.

When you deal with I/O, and specifically Socket I/O, you have to be prepared that any IOException would be thrown at you.

Some of them, like ClosedByInterruptException you may handle intelligently. Others, you may

  1. Add throws declarations to you method and let callers deal with IOExceptions
  2. Wrap IOExceptions into checked exception specific to your subsystem
  3. Wrap IOExceptions into RuntimeException specific or not to your subsystem
  4. Log IOException and continue.

In any case, once you get IOException, you probably cannot do much to communicate with that channel. So you may just close your connection and retry later.

BTW, read will only return -1 to you if you SUCCESSFULLY reached end-of-stream. In your case, I am sure, the connection is closed midstream.

EDIT in reply to OP comments

Can I prevent a channel to be interruptible?

No, this is a property of specific channel that you are using. If it implements InterruptibleChannel, then it IS interruptible, and will be closed upon receipt of thread interrupt. java.nio.channels.SocketChannel is interruptible

The docs for ClosedByInterruptException state: "Checked exception received by a thread when another thread interrupts it while it is blocked in an I/O operation upon a channel." Still how can it be blocking, if it is NON-blocking IO?

If you look at specification of ReadableByteChannel.read you will see that the method is declared to only throw IOException. Then in JavaDoc it hints what kind of specific IOExceptions and undre what conditions may be thrown by a standard java.nio implementation of this interface. This is one of the example of cooperative programming. The declarer of the interface tells the implementer how they should implement the method and what exceptions should they throw under what conditions.

For example, if I implement the read method in my exotic channel I may choose to totally ignore the specification and not throw any of exception in the declaration. The user of my class though will probably pay a heavy price when encountering unexpected behavior and will probably dump my implementation in favor of more robust one.

In any case, I think you are confused with how you should react to different exceptions declared in read method. First of all, not every exception in read method specification may be thrown. Case in point ClodesByInterruptException most likely will NOT get thrown in case you are using a NON-blocking I/O. On the other hand some implementers may choose to close the channel upon receipt of the interrupt and throw this exception when you attempt to read even if you are in NON-blocking I/O. But you really should not get concerned with that decision, because:

  1. You probably control whether current thread is interrupted or not
  2. This is just the other kind of IOException, and by default should be treated as fatal

And yes, the example IOEception is fatal, but my question is: Are they ALL?

Remember, that your framework should function normally, or shutdown gracefully when any kind of exception is thrown. For example your code, or library code at any point can throw unchecked ( Runtime ) exception. Only testing and more intimate knowledge of your environment can tell you which exceptions are truly fatal and which can be handled safely.

Because frameworks are written by people they also have bugs, deficiencies, etc. So there may be a case when IOException is thrown, but the underlying channel is still OK. Unfortunately these conditions can only be found with blood and guts in real production environment. Here is one of the example where IOException thrown by a socket can be totally ignored: http://bugs.sun.com/view_bug.do?bug_id=4516760.

So, start by writing generic handler for all IOExceptions and treat them all as fatal, relax it for specific conditions that you find out in production.

Alexander Pogrebnyak
+1 for a better answer than mine
Dancrumb
I know about these fallacies, and I respect them. What I don't understand is: What is the difference between *SUCCESSFULLY* reaching end-of-stream and the stream being closed "midstream". Note, that I am reading into a byte buffer and I am not trying to deserialsize anything or so. So physically there are simply no more bytes in the stream, when TCP FIN is received. That's it?
Steffen Heil
One more comment: Are you sure, that I cannot do anything any more the a socket, after I get any `IOException` on that? It this is safe, I will simply catch `IOException`s and handle them like I handle `ClosedByInterruptException` now: I am regarding this socket as being closed. Still i don't understand the following two points: why don't I get `-1` and why do `ClosedByInterruptException` s occur at all? None of my sockets are blocking, which seems to be suggested by the specification of `ClosedByInterruptException`.
Steffen Heil
@Steffen: If you are using a non-blocking I/O, are you sure that you have polled a selector, that there is something to read from the channel? If selector thinks there is nothing to read, you will definitely get an exception when try to do so. Also `ClosedByInterruptException` does not mean that your I/O is non-blocking. It only means that your channels are **interruptible**. About IOExceptions, in general, the ones thrown by a java.io or java.nio are more or less fatal, so treating your connection as **dead** is a good bet. The one you are getting, looks definitely fatal.
Alexander Pogrebnyak
Yes, I am very sure, that my read() can ONLY get called, if OP_READ was in readyOps(). Still there is a loop around that read(), and it should NOT throw an exception if there is nothing to read but return 0 (by spec). The docs for ClosedByInterruptException state: "Checked exception received by a thread when another thread interrupts it while it is blocked in an I/O operation upon a channel." Still how can it be blocking, if it is NON-blocking IO? Can I prevent an channel to be interruptible? And yes, the example IOEception is fatal, but my question is: Are they ALL?
Steffen Heil