Relates :
|
|
Relates :
|
|
Relates :
|
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) ======================================================================
|