FULL PRODUCT VERSION :
ADDITIONAL OS VERSION INFORMATION :
Both Windows XP and Linux.
A DESCRIPTION OF THE PROBLEM :
Taking a pair of ServerSocket and Socket, when server socket accepts connection and closes accepted socket, client Socket input stream read() returns immediately with -1, while it should throw :java.net.SocketException.
The appropriate part of API description of java.net.Socket says:
(...)
public InputStream getInputStream() throws IOException
Returns an input stream for this socket.
If this socket has an associated (...)
Under abnormal conditions the underlying connection may be broken by the remote host or the network software (for example a connection reset in the case of TCP connections). When a broken connection is detected by the network software the following applies to the returned input stream :-
The network software may discard bytes that are buffered by the socket. Bytes that aren't discarded by the network software can be read using read.
If there are no bytes buffered on the socket, or all buffered bytes have been consumed by read, then all ____ subsequent calls to read will throw an IOException. ____
If there are no bytes buffered on the socket, and the socket has not been closed using close, then available will return 0.
Closing the returned InputStream will close the associated socket.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run test case. Observe, that "Client:read()=-1 after 0[ms]" is written to System.out.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
As documentation says, it should throw an IOException exception.
I do believe it is currently "cast in stone" behavior (and in good agreement with InputStream contract) so I do not expect it to be fixed but I would like to see javadoc to reflect actual behavior instead of contain confusing sentences.
ACTUAL -
Does not follow java doc specs.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package sztejkat.utils;
import java.io.*;
import java.net.*;
/**
This class is a pattern how to detect broken socket connectio when timeouts are enabled.
*/
public class SocketBrokenDetector
{
public static void main(String [] args)throws Throwable
{
Thread server = new Thread()
{
public void run()
{
try{
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress("localhost",1879));
ss.setSoTimeout(5000);
Socket accepted = ss.accept();
if (accepted!=null)
{
System.out.println("Server:accepted");
accepted.getOutputStream().write(0);
System.out.println("Server:sleeping");
/*
We do observe, that if we do close accepted socket
the reader side does not see broken connection.
Instead it returns -1 from read immediately.
*/
Thread.sleep(15000);
//accepted.getOutputStream().flush(); //this does not help.
//accepted.shutdownOutput(); //this does not help.
accepted.close();
System.out.println("Server:closed");
};
}catch(Exception ex)
{
System.out.println("Server failed:"+ex);
ex.printStackTrace(System.out);
};
};
};
Thread client = new Thread()
{
public void run()
{
try{
for(;;)
{
Socket ss = new Socket();
ss.setSoTimeout(5000);
ss.connect(new InetSocketAddress("localhost",1879),5000);
for(;;)
{
long start = System.currentTimeMillis();
try{
/*
This method should read zero right after connect and
then should fail after 5 seconds, what would mean-timeout,
but connection is fine.
Then it _should_ throw an exception at read.
*/
int readen = ss.getInputStream().read();
/*
byte [] buff = new byte[3];
int readen = ss.getInputStream().read(buff);
does behave the same way, returns -1
*/
long stop = System.currentTimeMillis();
long delta =(stop-start) & 0x7FFFFFFFFFFFFFFFl;
System.out.println("Clinet.read()="+readen+" after "+delta+"[ms]");
}catch(Exception ex)
{
long stop = System.currentTimeMillis();
long delta =(stop-start) & 0x7FFFFFFFFFFFFFFFl;
System.out.println("Clinet:"+ex+" after "+delta+"[ms]");
};
Thread.sleep(500);
}
}
}catch(Exception ex)
{
System.out.println("Server failed:"+ex);
ex.printStackTrace(System.out);
};
};
};
server.start();
client.start();
};
};
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Different coding for InputStreams from different sources must be used, or streams have to be wrapped to provide a consistent behavior.
For example, piped streams do throw an exception when other side pipe is closed. Socket will return -1 while it will throw on timeout. The gnu.io and an old javax.comm returns -1 on timeout and throws on closed.
Similar situation occurs in partial read requests and no consideration of timeouts when doing buffered read-ahead. The InputStreamReader is a good example of that. Try it with timeouted stream and observe how it freeze.
All the write-once-use-may philosophy gone to a trash...
Personally I would expect an operation on un-usable stream to throw an exception, since it is a serious and non-recoverable error, while operation on usable stream to return -1 or partial read on timeout, since timeout is an expected, recoverable minor signal saying "working fine, but currently no more data".
I would also expect a consistent and expected timeout policy, broken connection policy and other important things to be clearly stated and explained in InputStream and OutputStream javadoc contracts.
Anyway.... Thanks for Java! It's a great tool, even considering some minor bugs. Thanks to largely open source approach in core libraries it is relatively easy to find what is not documented.
ADDITIONAL OS VERSION INFORMATION :
Both Windows XP and Linux.
A DESCRIPTION OF THE PROBLEM :
Taking a pair of ServerSocket and Socket, when server socket accepts connection and closes accepted socket, client Socket input stream read() returns immediately with -1, while it should throw :java.net.SocketException.
The appropriate part of API description of java.net.Socket says:
(...)
public InputStream getInputStream() throws IOException
Returns an input stream for this socket.
If this socket has an associated (...)
Under abnormal conditions the underlying connection may be broken by the remote host or the network software (for example a connection reset in the case of TCP connections). When a broken connection is detected by the network software the following applies to the returned input stream :-
The network software may discard bytes that are buffered by the socket. Bytes that aren't discarded by the network software can be read using read.
If there are no bytes buffered on the socket, or all buffered bytes have been consumed by read, then all ____ subsequent calls to read will throw an IOException. ____
If there are no bytes buffered on the socket, and the socket has not been closed using close, then available will return 0.
Closing the returned InputStream will close the associated socket.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run test case. Observe, that "Client:read()=-1 after 0[ms]" is written to System.out.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
As documentation says, it should throw an IOException exception.
I do believe it is currently "cast in stone" behavior (and in good agreement with InputStream contract) so I do not expect it to be fixed but I would like to see javadoc to reflect actual behavior instead of contain confusing sentences.
ACTUAL -
Does not follow java doc specs.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package sztejkat.utils;
import java.io.*;
import java.net.*;
/**
This class is a pattern how to detect broken socket connectio when timeouts are enabled.
*/
public class SocketBrokenDetector
{
public static void main(String [] args)throws Throwable
{
Thread server = new Thread()
{
public void run()
{
try{
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress("localhost",1879));
ss.setSoTimeout(5000);
Socket accepted = ss.accept();
if (accepted!=null)
{
System.out.println("Server:accepted");
accepted.getOutputStream().write(0);
System.out.println("Server:sleeping");
/*
We do observe, that if we do close accepted socket
the reader side does not see broken connection.
Instead it returns -1 from read immediately.
*/
Thread.sleep(15000);
//accepted.getOutputStream().flush(); //this does not help.
//accepted.shutdownOutput(); //this does not help.
accepted.close();
System.out.println("Server:closed");
};
}catch(Exception ex)
{
System.out.println("Server failed:"+ex);
ex.printStackTrace(System.out);
};
};
};
Thread client = new Thread()
{
public void run()
{
try{
for(;;)
{
Socket ss = new Socket();
ss.setSoTimeout(5000);
ss.connect(new InetSocketAddress("localhost",1879),5000);
for(;;)
{
long start = System.currentTimeMillis();
try{
/*
This method should read zero right after connect and
then should fail after 5 seconds, what would mean-timeout,
but connection is fine.
Then it _should_ throw an exception at read.
*/
int readen = ss.getInputStream().read();
/*
byte [] buff = new byte[3];
int readen = ss.getInputStream().read(buff);
does behave the same way, returns -1
*/
long stop = System.currentTimeMillis();
long delta =(stop-start) & 0x7FFFFFFFFFFFFFFFl;
System.out.println("Clinet.read()="+readen+" after "+delta+"[ms]");
}catch(Exception ex)
{
long stop = System.currentTimeMillis();
long delta =(stop-start) & 0x7FFFFFFFFFFFFFFFl;
System.out.println("Clinet:"+ex+" after "+delta+"[ms]");
};
Thread.sleep(500);
}
}
}catch(Exception ex)
{
System.out.println("Server failed:"+ex);
ex.printStackTrace(System.out);
};
};
};
server.start();
client.start();
};
};
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Different coding for InputStreams from different sources must be used, or streams have to be wrapped to provide a consistent behavior.
For example, piped streams do throw an exception when other side pipe is closed. Socket will return -1 while it will throw on timeout. The gnu.io and an old javax.comm returns -1 on timeout and throws on closed.
Similar situation occurs in partial read requests and no consideration of timeouts when doing buffered read-ahead. The InputStreamReader is a good example of that. Try it with timeouted stream and observe how it freeze.
All the write-once-use-may philosophy gone to a trash...
Personally I would expect an operation on un-usable stream to throw an exception, since it is a serious and non-recoverable error, while operation on usable stream to return -1 or partial read on timeout, since timeout is an expected, recoverable minor signal saying "working fine, but currently no more data".
I would also expect a consistent and expected timeout policy, broken connection policy and other important things to be clearly stated and explained in InputStream and OutputStream javadoc contracts.
Anyway.... Thanks for Java! It's a great tool, even considering some minor bugs. Thanks to largely open source approach in core libraries it is relatively easy to find what is not documented.