Why do IOExceptions occur in ReadableByteChannel.read()
The specification of ReadableByteChannel.read()
shows -1
as result 开发者_运维问答value for end-of-stream. Moreover it specifies ClosedByInterruptException
as 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:
- Why is this exception thrown in the first place?
- Is it safe to assume that ANY exception thrown by read are about the socket being closed?
- 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
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
- Add
throws
declarations to you method and let callers deal with IOExceptions - Wrap IOExceptions into checked exception specific to your subsystem
- Wrap IOExceptions into RuntimeException specific or not to your subsystem
- 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:
- You probably control whether current thread is interrupted or not
- 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.
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.
精彩评论