Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4726957

(so) Socket.close fails if timeout set on Socket created from SocketChannel

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P2 P2
    • 6
    • spider, 1.3.0, 1.4.1, 1.4.2_05
    • core-libs
    • b33
    • generic, other, x86, sparc
    • generic, other, linux, solaris

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

              sherman Xueming Shen
              rmandalasunw Ranjith Mandala (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: