|
Description
|
FULL PRODUCT VERSION :
java version "1.4.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_01-b03)
Java HotSpot(TM) Client VM (build 1.4.0_01-b03, mixed mode)
FULL OPERATING SYSTEM VERSION :
Linux piglet 2.4.18-5ipsec #1 Mon Jun 24 03:53:03 PDT 2002
i686 unknown
Red Hat Linux release 7.3 (Valhalla)
ADDITIONAL OPERATING SYSTEMS :
This is applies to all platforms.
A DESCRIPTION OF THE PROBLEM :
Preface - The real problem is that the java.net.Socket specification doesn't
specify that closing the input stream closes the socket. So this example
performs to spec, but the spec is weak and doesn't cover this scenario.
SocketInputStream and SocketOutputStream close the
underlying socket when their close() methods are called.
This is unintuitive since socket.getInputStream().close()
shoud have the same semantics as socket.shutdownInput() and
similarly for socket.getOutputStream()close().
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the submitted program.
EXPECTED VERSUS ACTUAL BEHAVIOR :
Closing the OutputStream of a socket should not affect the
InputStream or vice versa.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.SocketException: null fd customer
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:116)
at sun.nio.cs.StreamDecoder$CharsetSD.readBytes(StreamDecoder.java:404)
at sun.nio.cs.StreamDecoder$CharsetSD.implRead(StreamDecoder.java:442)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:179)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.BufferedReader.fill(BufferedReader.java:136)
at java.io.BufferedReader.readLine(BufferedReader.java:299)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at Client.run(SocketTest.java:41)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.net.*;
class Server extends Thread
{
public void run() {
try {
System.err.println("listen");
ServerSocket socket = new ServerSocket(SocketTest.PORT);
System.err.println("Accept");
Socket client = socket.accept();
PrintStream out = new PrintStream(client.getOutputStream());
for (int i = 0; ; ++i) {
out.println("*************** msg "+ i +"******************");
}
}
catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
class Client extends Thread
{
public void run() {
try {
sleep(500);
System.err.println("Connect");
Socket socket =
new Socket("localhost", SocketTest.PORT);
BufferedReader reader = new BufferedReader
(new InputStreamReader(socket.getInputStream()));
for (int i = 0; ; ++i) {
System.out.println(reader.readLine());
if (i == 10)
socket.getOutputStream().close();
}
}
catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
public class SocketTest {
public static final int PORT = 55555;
public static void main(String[] args)
throws Exception {
Thread server = new Server();
Thread client = new Client();
server.start();
client.start();
System.err.println("Threads started");
server.join();
client.join();
}
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
Use socket.shutdownInput() or socket.shutdownOutput() to
unidirectionally close a socket.
(Review ID: 159423)
======================================================================
Posted Date : 2006-01-10 06:19:41.0
Suggested fix from java.net member leouser:
A DESCRIPTION OF THE FIX :
BUG ID 4717638 Socket's getInputStream().close doesn't specify that it closes socket.
FILES AFFECTED: java.net.Socket
JDK: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
DISCUSION( embedded in test case):
/**
* BUG ID: 4717638 Sockets's getInputStream().close() doesn't specify that it closes socket
* This JavaDoc bug pertains not to just the input stream, but the output stream
* and the channel. There are 2 avenues of attack here:
* 1. Alter the behavior of the close calls on the streams
* 2. Alter the javadoc so the close call's behavior is clear.
*
* I have discarded 1 because it is too radical to have existing clients
* that been built around the behavior of close() to suddenly not have this behavior.
* There are times when altering the behavior may be ok, this is not one of them
* in my opinion.
*
* Hence we choose 2, which essentially is just stating that calling 'close' on
* any of the streams/channels closes the Socket. Not that hard to add and it
* will result in removing any surprises associated with calling close. Ideally
* when methods return an interface and that interfaces methods do more than what
* they contract to do, it would be good for the returning method to document
* the enhancements and deviations from the contract. Otherwise it is up to
* the programmer to discover the behavior by observation, not good.
*
* ANTI-RATIONALE:
* 1. The Socket will be locked into this behavior since the contract now
* states that this is how it operates. But I refer you to to the rejection
* of avenue 1 to understand why we are at liberty here to alter the doc.
*
* TESTING PLAN:
* Prove that calling close on the streams/channels close the Socket, hence
* proving that the JavaDoc is now accurate.
*
* FILES AFFECTED: java.net.Socket
* JAVA VERSION: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
* Ran and tested on a Suse 7.3 distribution.
*
* Brian Harry
* xxxxx@xxxxx
* Jan 9, 2006
*/
unified diff:
--- /home/nstuff/java6/jdk1.6.0/java/net/Socket.java Thu Dec 15 02:16:45 2005
+++ /home/javarefs/java/net/Socket.java Mon Jan 9 12:10:18 2006
@@ -704,6 +704,11 @@
* java.nio.channels.ServerSocketChannel#accept ServerSocketChannel.accept}
* methods.
*
+ * <p> A SocketChannel that is acquired from the Socket instance and
+ * is subsequently closed will in turn close the Socket. Hence, to
+ * keep the Socket instance open, protect against the SocketChannel
+ * from being closed.
+ *
* @return the socket channel associated with this socket,
* or <tt>null</tt> if this socket was not created
* for a channel
@@ -748,6 +753,11 @@
*
* </ul>
*
+ * <p> An InputStream that is acquired from the Socket instance and
+ * is subsequently closed will in turn close the Socket. Hence, to
+ * keep the Socket instance open, protect against the InputStream
+ * from being closed.
+ *
* @return an input stream for reading bytes from this socket.
* @exception IOException if an I/O error occurs when creating the
* input stream, the socket is closed, the socket is
@@ -787,6 +797,11 @@
* is in non-blocking mode then the output stream's <tt>write</tt>
* operations will throw an {@link
* java.nio.channels.IllegalBlockingModeException}.
+ *
+ * <p> An OutputStream that is acquired from the Socket instance and
+ * is subsequently closed will in turn close the Socket. Hence, to
+ * keep the Socket instance open, protect against the OutputStream from
+ * being closed.
*
* @return an output stream for writing bytes to this socket.
* @exception IOException if an I/O error occurs when creating the
JUnit TESTCASE :
import junit.framework.TestCase;
import junit.textui.TestRunner;
import static java.lang.System.out;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
/**
* BUG ID: 4717638 Sockets's getInputStream().close() doesn't specify that it closes socket
* This JavaDoc bug pertains not to just the input stream, but the output stream
* and the channel. There are 2 avenues of attack here:
* 1. Alter the behavior of the close calls on the streams
* 2. Alter the javadoc so the close call's behavior is clear.
*
* I have discarded 1 because it is too radical to have existing clients
* that been built around the behavior of close() to suddenly not have this behavior.
* There are times when altering the behavior may be ok, this is not one of them
* in my opinion.
*
* Hence we choose 2, which essentially is just stating that calling 'close' on
* any of the streams/channels closes the Socket. Not that hard to add and it
* will result in removing any surprises associated with calling close. Ideally
* when methods return an interface and that interfaces methods do more than what
* they contract to do, it would be good for the returning method to document
* the enhancements and deviations from the contract. Otherwise it is up to
* the programmer to discover the behavior by observation, not good.
*
* ANTI-RATIONALE:
* 1. The Socket will be locked into this behavior since the contract now
* states that this is how it operates. But I refer you to to the rejection
* of avenue 1 to understand why we are at liberty here to alter the doc.
*
* TESTING PLAN:
* Prove that calling close on the streams/channels close the Socket, hence
* proving that the JavaDoc is now accurate.
*
* FILES AFFECTED: java.net.Socket
* JAVA VERSION: jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
* Ran and tested on a Suse 7.3 distribution.
*
* Brian Harry
* xxxxx@xxxxx
* Jan 9, 2006
*/
public class TestSocket extends TestCase{
public TestSocket(String method){
super(method);
}
public void testSocketDocIsAccurate(){
out.println();
try{
Socket s1 = new Socket( "127.0.0.1", 10000);
out.println( s1 + " is closed? " + s1.isClosed());
InputStream ins = s1.getInputStream();
ins.close();
assertEquals( true, s1.isClosed());
out.println("InputStream close, closed the socket? " + s1.isClosed());
}
catch(IOException io){
io.printStackTrace();
}
try{
Socket s2 = new Socket( "127.0.0.1", 10000);
out.println( s2 + " is closed? "+ s2.isClosed());
OutputStream os = s2.getOutputStream();
os.close();
assertEquals( true, s2.isClosed());
out.println("OutputStram close, closed the socket? " + s2.isClosed());
}
catch(IOException io){
io.printStackTrace();
}
try{
InetSocketAddress isa = new InetSocketAddress(10000);
SocketChannel sc = SocketChannel.open(isa);
Socket s3 = sc.socket();
out.println( s3 + " is closed? " + s3.isClosed());
SocketChannel sc2 = s3.getChannel();
sc2.close();
assertEquals( true, s3.isClosed());
out.println("SocketChannel close, closed the socket? " + s3.isClosed());
}
catch(IOException io){
io.printStackTrace();
}
}
public static void main(String ... args){
Thread t = new Thread(){
public void run(){
try{
ServerSocket ss = new ServerSocket(10000);
int i = 3;
while(i != 0){
ss.accept();
i--;
}
sleep(1000);//enough time for the other thread to complete.
}
catch(IOException io){ io.printStackTrace(); }
catch(InterruptedException ie){}
}
};
t.start();
try{
Thread.currentThread().sleep(3000);
}
catch(InterruptedException ie){}
TestCase tc = new TestSocket("testSocketDocIsAccurate");
TestRunner.run(tc);
}
}
FIX FOR BUG NUMBER:
4717638
Posted Date : 2006-01-10 06:19:41.0
|
|
Comments
|
Submitted On 26-JAN-2004
pifpafpuf
> The behaviour is correct
Why?
> and getInputStream().close()has always closed the socket.
I hope this is not the the reason why the current behaviour
is thought to be correct, because it might have always been
wrong.
Indeed the current behaviour of closing the socket when the
InputStream is closed is a very bad side effect when such
an InputStream is used in Socket-agnostic code.
Such code should be free, or rather even has the obligation,
to close the InputStream (soon) after it got EOF. But this then
prevents any pending data to be send down the OutputStream
of the socket. As a result, fairly unrelated processing
is influenced and the neat abstraction an InputStream
represents is broken.
At least, the method should not have the signature
InputStream getInputStream()
but rather
SocketInputStream getInputStream()
to signal that the returned InputStream is not usable in
a general InputStream-context.
A better solution would of course be to change the
behaviour to be in line with shutdownInput as proposed
in the original bug report.
Harald Kirsch
Submitted On 01-MAR-2004
pifpafpuf
A similar problem exists of course with getOutputStream().
Assume two threads, one feeding a server, the other retrieving
results. The server works as a filter which permanently
produces output as it gets input (like a pipe on the command
line). The first client thread will
eventually call getOutputStream() on the socket and may
pass the result to a method which only copies characters
to a given OutputStream. Consequently this method will do
PrintStream p = new PrintStream(the socket's output stream)
p.print(...)
When it is finished, calls p.close(). Bang. This closes
the sockets output stream, which closes the socket which
potentially prevents the other thread to read the rest of
the filtered data. Very bad.
Harald Kirsch
Submitted On 04-JUN-2009
samokhodkin
> and getInputStream().close() has always closed the
socket
The getOutputStream().close() in the JDK 1.3, DIDN'T close the socket, it just sent a FIN tho the othe side. I know it for sure, because my networking programs stopped working just because of it, back in the times when I switched from 1.3 to 1.4. And I'd say, the 1.3 behaviour is a much better design.
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|