JDK-6232954 : (so) client does not see (NIO-created) socket close with SO_TIMEOUT
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.2_06
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2005-02-24
  • Updated: 2010-04-02
  • Resolved: 2005-04-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
1.4.2_10Fixed 6 b33Fixed
Description
Steps:
[1] create a socket from ServerSocketChannel.accept; configure it to be blocking;
[2] set a non-zero socket SO_TIMEOUT
[3] read a byte from the socket's InputStream
[4] close the socket (or the socketchannel)
[**] the client does not receive the socket being closed immediately

We need to set SO_TIMEOUT so that read on the (blocking) socket does not wait indefinitely.

Below is the test case to reproduce this.  The client used is (linux) telnet.  We could reproduce the problem with jdk 1.4.2_03, 1.4.2_06, jdk 1.5 on window XP and jdk 1.4 on linux.


import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.net.Socket;
import java.nio.channels.*;
import java.util.Iterator;

public class B {
    //    static final public boolean USE_A_NEW_THREAD = Boolean.getBoolean("use.a.new.thread");
    static final public boolean USE_A_NEW_THREAD = true;

    private SocketAddress endpoint;
    private ServerSocketChannel serverSocketChannel;
    private ServerSocket serverSocket;
    private Selector selector;


    public B() {
    }


    public void connect() throws IOException {
        serverSocketChannel = ServerSocketChannel.open();
        serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(9000), 100);
        serverSocketChannel.configureBlocking(false);
        selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }


    public void select() throws IOException {
        while (true) {
            try {
                if (selector.select(10 * 60 * 1000) == 0) {  // 10 minutes
                    System.out.print("nothing being selected..........");
                    return;
                }
            } catch (ClosedSelectorException e) {
                e.printStackTrace(System.out);
                return;
            } catch (NullPointerException e) {
                e.printStackTrace(System.out);
                return;
            }

            Iterator _iterator = selector.selectedKeys().iterator();
            for (; _iterator.hasNext();) {
                SelectionKey _key = (SelectionKey) _iterator.next();
                _iterator.remove();
                if (_key.isAcceptable()) {
                    handleAccept(_key);
                } else if (_key.isReadable()) {
                    handleRead(_key);
                } else {
                    System.out.println("Unhandled key state: " + _key);
                }
            }
        }
    }


    protected void handleAccept(SelectionKey _key) throws IOException {
        ServerSocketChannel _server = (ServerSocketChannel) _key.channel();
        SocketChannel _clientSocketChannel = _server.accept();
        Handler _handler = new Handler(this, _clientSocketChannel);
        _clientSocketChannel.configureBlocking(true);
        Socket s = _clientSocketChannel.socket();
        s.setTcpNoDelay(true);
        s.setKeepAlive(true);
        s.setSoTimeout(1000000);

        if (USE_A_NEW_THREAD) {
            System.out.println("a new thread is spawned");
            (new Thread(_handler)).start();
        } else {
            _handler.run();
        }
    }


    protected void handleRead(SelectionKey _key) throws IOException {
        throw new IllegalStateException("......... not implemented ..........");
    }


    public static class Handler implements Runnable {
        private SocketChannel channel;
        private B driver;


        public Handler(B _driver, SocketChannel _channel) {
            driver = _driver;
            channel = _channel;
        }


        public void run() {
            try {
                System.out.println("invoke setSoTimeout");

                InputStream is = channel.socket().getInputStream();
                int b = is.read();


                System.out.println("sleep 5 seconds and then close the channel");
                Thread.sleep(1000 * 5);
                System.out.println("socket to be closed: " + channel.socket());
                System.out.println("socket to be closed: " + channel.socket());
                System.out.println("socket to be closed: " + channel.socket());


                // channel.socket().close();
                channel.close();
                //                channel.close();
                System.out.println("connection closed");
            } catch (Exception e) {
                e.printStackTrace(System.out);
            }
        }
    }


    final static public void main(String[] argv) {
        try {
            B b = new B();
            b.connect();
            b.select();
            Thread.sleep(1000 * 60 * 20); // twenty minutes
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
}
###@###.### 2005-2-24 23:54:47 GMT

Comments
EVALUATION two issues here, (1)preClose() in *SocketChannel classes need to close the socket see#4960962 (2)timeout impl in *SocketAdaptor classes need to throw correct IOException after wakeup by "close" ###@###.### 2005-03-12 05:29:31 GMT
2005-03-12