Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-2131210 | 5.0u7 | Xueming Shen | P3 | Resolved | Fixed | b01 |
Name: rmT116609 Date: 08/06/2002
FULL PRODUCT VERSION :
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b14)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b14, mixed mode)
FULL OPERATING SYSTEM VERSION :
Linux 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
Red Hat Linux release 7.1 (Seawolf)
glibc-2.2.2-10
ADDITIONAL OPERATING SYSTEMS :
Windows NT 4.0
A DESCRIPTION OF THE PROBLEM :
Socket.close() does not work if the socket was created from
SocketChannel.open() and ever had a read timeout set via
setSoTimeout(). Under these circumstances, close() does not
send a FIN packet. This happens on both Linux and Windows
NT. Closer investigation with strace on Linux shows that
neither the close() nor shutdown() system calls are being
called.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code. This connects a client C to a server
S and simulates a simple protocol:
-C sends a message to S
-S sends a message to C
-C sends a message to S
-C closes the connection
The important thing is that C is created via
SocketChannel.open(..), though C does not use non-blocking
IO. Also, C calls setSoTimeout before reading each message
from S, but the read never times out.
The details of the server thread are mostly irrelevant.
This server can run in a different process on another
machine. However, adding one carefully placed delay in the
server does seem to exacerbate the problem.
EXPECTED VERSUS ACTUAL BEHAVIOR :
Below is the expected output. Messages from the client are
shown on the left. Message from the server are shown on the
right.
Establishing connection
1. Writing byte 1
Listening on port 6347
Accepted client
Read byte 1
2. Writing byte 2
Read byte 2
3. Writing byte 3
Read byte 3
Closing
Read byte EOF
Closing
At this point, both threads should terminate and the JVM
should exit. However, running the code actually results in
the following output:
Establishing connection
1. Writing byte 1
Listening on port 6347
Accepted client
Read byte 1
2. Writing byte 2
Read byte 2
3. Writing byte 3
Read byte 3
Closing
[hangs]
Note that the server never reads the EOF. Hence the server
thread hangs and the JVM does not terminate. Investigation
with tcpdump shows that the client never sent a FIN segment.
I've omitted several fields in the output below for
clarity. Time is shown in milliseconds.
13 lo > me.4427 > me.6347: S 2170895982:2170895982(0)
win 32767
13 lo > me.6347 > me.4427: S 2163215961:2163215961(0)
ack 2170895983 win 32767
13 lo > me.4427 > me.6347: . 1:1(0) ack 1 win 32767
15 lo > me.4427 > me.6347: P 1:2(1) ack 1 win 32767
15 lo > me.6347 > me.4427: . 1:1(0) ack 2 win 32767
23 lo > me.6347 > me.4427: P 1:2(1) ack 2 win 32767
23 lo > me.4427 > me.6347: . 2:2(0) ack 2 win 32767
24 lo > me.4427 > me.6347: P 2:3(1) ack 2 win 32767
24 lo > me.6347 > me.4427: . 2:2(0) ack 3 win 32767
If the program were working properly (e.g., if setSoTimeout
or SocketChannel.open were not used) the following
additional segments would sent:
25 lo < me.4430 > me.6347: F 3:3(0) ack 2 win 32767
25 lo < me.6347 > me.4430: F 2:2(0) ack 4 win 32767
25 lo < me.4430 > me.6347: . 4:4(0) ack 3 win 32767
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.nio.*;
import java.net.*;
import java.nio.channels.*;
/**
* Tests whether socket close always result in a FIN packet.
*/
public class SocketCloseTest {
final static int PORT=6347;
public static void main(String args[]) {
//Start listening thread.
try {
ServerSocketChannel listener=ServerSocketChannel.open();
listener.socket().bind(new InetSocketAddress(PORT));
AcceptorThread thread=new AcceptorThread(listener);
thread.start();
} catch (IOException e) {
System.out.println("Mysterious IO problem");
e.printStackTrace();
System.exit(1);
}
//Establish connection. Bug only happens if we open with channel.
try {
System.out.println("Establishing connection");
Socket socket=SocketChannel.open(
new InetSocketAddress("127.0.0.1", PORT)).socket();
OutputStream out=socket.getOutputStream();
InputStream in=socket.getInputStream();
System.out.println("1. Writing byte 1");
out.write((byte)1);
int n=read(socket, in);
System.out.println("Read byte "+n+"\n");
System.out.println("3. Writing byte 3");
out.write((byte)3);
System.out.println("Closing");
socket.close();
} catch (IOException e) {
System.out.println("Mysterious IO problem");
e.printStackTrace();
System.exit(1);
}
}
/** Reads one byte from in, which must be s.getInputStream. */
private static int read(Socket s, InputStream in) throws IOException {
try {
s.setSoTimeout(8000); //causes a bug!
return in.read();
} finally {
s.setSoTimeout(0);
}
}
}
/** Server thread */
class AcceptorThread extends Thread {
final String INDENT="\t\t\t\t";
ServerSocketChannel _listener;
/** @param listener MUST be bound to a port */
AcceptorThread(ServerSocketChannel listener) {
_listener=listener;
}
public void run() {
try {
//This sleep isn't strictly necessary but seems to make the bug more
//repeatable.
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
System.out.println(INDENT+"Listening on port "
+SocketCloseTest.PORT);
ByteBuffer buf=ByteBuffer.allocate(5);
Socket client=_listener.accept().socket();;
System.out.println(INDENT+"Accepted client");
OutputStream out=client.getOutputStream();
InputStream in=client.getInputStream();
int n=in.read();
System.out.println(INDENT+"Read byte "+n+"\n");
System.out.println(INDENT+"2. Writing byte 2");
out.write((byte)2);
n=in.read();
System.out.println(INDENT+"Read byte "+n+"\n");
n=in.read();
System.out.println(INDENT+"Read byte "
+(n<0 ? "EOF" : Integer.toString(n)));
System.out.println(INDENT+"Closing");
client.close();
} catch (IOException e) {
System.out.println(INDENT+"Error accepting!");
}
}
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
-use Socket.shutdownOutput() and shutdownInput() instead of
close(). The former results in a FIN segment.
-use "new Socket(..)" instead of SocketChannel.open(..) if
non-blocking IO is not needed
-don't use setSoTimeout() if not needed
(Review ID: 160421)
======================================================================
FULL PRODUCT VERSION :
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b14)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b14, mixed mode)
FULL OPERATING SYSTEM VERSION :
Linux 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
Red Hat Linux release 7.1 (Seawolf)
glibc-2.2.2-10
ADDITIONAL OPERATING SYSTEMS :
Windows NT 4.0
A DESCRIPTION OF THE PROBLEM :
Socket.close() does not work if the socket was created from
SocketChannel.open() and ever had a read timeout set via
setSoTimeout(). Under these circumstances, close() does not
send a FIN packet. This happens on both Linux and Windows
NT. Closer investigation with strace on Linux shows that
neither the close() nor shutdown() system calls are being
called.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code. This connects a client C to a server
S and simulates a simple protocol:
-C sends a message to S
-S sends a message to C
-C sends a message to S
-C closes the connection
The important thing is that C is created via
SocketChannel.open(..), though C does not use non-blocking
IO. Also, C calls setSoTimeout before reading each message
from S, but the read never times out.
The details of the server thread are mostly irrelevant.
This server can run in a different process on another
machine. However, adding one carefully placed delay in the
server does seem to exacerbate the problem.
EXPECTED VERSUS ACTUAL BEHAVIOR :
Below is the expected output. Messages from the client are
shown on the left. Message from the server are shown on the
right.
Establishing connection
1. Writing byte 1
Listening on port 6347
Accepted client
Read byte 1
2. Writing byte 2
Read byte 2
3. Writing byte 3
Read byte 3
Closing
Read byte EOF
Closing
At this point, both threads should terminate and the JVM
should exit. However, running the code actually results in
the following output:
Establishing connection
1. Writing byte 1
Listening on port 6347
Accepted client
Read byte 1
2. Writing byte 2
Read byte 2
3. Writing byte 3
Read byte 3
Closing
[hangs]
Note that the server never reads the EOF. Hence the server
thread hangs and the JVM does not terminate. Investigation
with tcpdump shows that the client never sent a FIN segment.
I've omitted several fields in the output below for
clarity. Time is shown in milliseconds.
13 lo > me.4427 > me.6347: S 2170895982:2170895982(0)
win 32767
13 lo > me.6347 > me.4427: S 2163215961:2163215961(0)
ack 2170895983 win 32767
13 lo > me.4427 > me.6347: . 1:1(0) ack 1 win 32767
15 lo > me.4427 > me.6347: P 1:2(1) ack 1 win 32767
15 lo > me.6347 > me.4427: . 1:1(0) ack 2 win 32767
23 lo > me.6347 > me.4427: P 1:2(1) ack 2 win 32767
23 lo > me.4427 > me.6347: . 2:2(0) ack 2 win 32767
24 lo > me.4427 > me.6347: P 2:3(1) ack 2 win 32767
24 lo > me.6347 > me.4427: . 2:2(0) ack 3 win 32767
If the program were working properly (e.g., if setSoTimeout
or SocketChannel.open were not used) the following
additional segments would sent:
25 lo < me.4430 > me.6347: F 3:3(0) ack 2 win 32767
25 lo < me.6347 > me.4430: F 2:2(0) ack 4 win 32767
25 lo < me.4430 > me.6347: . 4:4(0) ack 3 win 32767
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.io.*;
import java.nio.*;
import java.net.*;
import java.nio.channels.*;
/**
* Tests whether socket close always result in a FIN packet.
*/
public class SocketCloseTest {
final static int PORT=6347;
public static void main(String args[]) {
//Start listening thread.
try {
ServerSocketChannel listener=ServerSocketChannel.open();
listener.socket().bind(new InetSocketAddress(PORT));
AcceptorThread thread=new AcceptorThread(listener);
thread.start();
} catch (IOException e) {
System.out.println("Mysterious IO problem");
e.printStackTrace();
System.exit(1);
}
//Establish connection. Bug only happens if we open with channel.
try {
System.out.println("Establishing connection");
Socket socket=SocketChannel.open(
new InetSocketAddress("127.0.0.1", PORT)).socket();
OutputStream out=socket.getOutputStream();
InputStream in=socket.getInputStream();
System.out.println("1. Writing byte 1");
out.write((byte)1);
int n=read(socket, in);
System.out.println("Read byte "+n+"\n");
System.out.println("3. Writing byte 3");
out.write((byte)3);
System.out.println("Closing");
socket.close();
} catch (IOException e) {
System.out.println("Mysterious IO problem");
e.printStackTrace();
System.exit(1);
}
}
/** Reads one byte from in, which must be s.getInputStream. */
private static int read(Socket s, InputStream in) throws IOException {
try {
s.setSoTimeout(8000); //causes a bug!
return in.read();
} finally {
s.setSoTimeout(0);
}
}
}
/** Server thread */
class AcceptorThread extends Thread {
final String INDENT="\t\t\t\t";
ServerSocketChannel _listener;
/** @param listener MUST be bound to a port */
AcceptorThread(ServerSocketChannel listener) {
_listener=listener;
}
public void run() {
try {
//This sleep isn't strictly necessary but seems to make the bug more
//repeatable.
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
System.out.println(INDENT+"Listening on port "
+SocketCloseTest.PORT);
ByteBuffer buf=ByteBuffer.allocate(5);
Socket client=_listener.accept().socket();;
System.out.println(INDENT+"Accepted client");
OutputStream out=client.getOutputStream();
InputStream in=client.getInputStream();
int n=in.read();
System.out.println(INDENT+"Read byte "+n+"\n");
System.out.println(INDENT+"2. Writing byte 2");
out.write((byte)2);
n=in.read();
System.out.println(INDENT+"Read byte "+n+"\n");
n=in.read();
System.out.println(INDENT+"Read byte "
+(n<0 ? "EOF" : Integer.toString(n)));
System.out.println(INDENT+"Closing");
client.close();
} catch (IOException e) {
System.out.println(INDENT+"Error accepting!");
}
}
}
---------- END SOURCE ----------
CUSTOMER WORKAROUND :
-use Socket.shutdownOutput() and shutdownInput() instead of
close(). The former results in a FIN segment.
-use "new Socket(..)" instead of SocketChannel.open(..) if
non-blocking IO is not needed
-don't use setSoTimeout() if not needed
(Review ID: 160421)
======================================================================
- backported by
-
JDK-2131210 (so) Socket.close fails if timeout set on Socket created from SocketChannel
- Resolved
- relates to
-
JDK-4724030 (so) Async close of socket stream fails when timeout specified
- Resolved
-
JDK-4727975 (so) Cannot close a channel-based socket if timeout specified
- Closed
-
JDK-6215050 (so) SocketChannel created in CLOSE_WAIT and never cleaned up.. File Descriptor leak
- Resolved