Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4726957
Votes 4
Synopsis (so) Socket.close fails if timeout set on Socket created from SocketChannel
Category java:classes_nio
Reported Against 1.3 , b34 , 1.4.1 , 1.4.2_05 , hopper-beta
Release Fixed mustang(b33), 5.0u7(b01) (Bug ID:2131210)
State 10-Fix Delivered, bug
Priority: 2-High
Related Bugs 6375656 , 4724030 , 4727975 , 6215050
Submit Date 06-AUG-2002
Description


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) 
======================================================================
Posted Date : 2005-10-26 21:55:14.0
Work Around
N/A
Evaluation
Essentially the same as 4724030.  --   xxxxx@xxxxx   2002/8/7
Comments
  
  Include a link with my name & email   

Submitted On 21-JAN-2004
kurti
This still happens with 1.4.2; here is my example:

import java.io.*;
import java.net.*;
import java.nio.channels.*;

public class Test {

    public static void main(String[] args) {
        int socketTimeout = 10000;  // set this to 0 and it
works
        boolean workaround = false; // set this to true and
it works
        
        try {
            ServerSocketChannel ssc =
ServerSocketChannel.open();
            ServerSocket ss = ssc.socket();
            ss.bind(new InetSocketAddress(2525));
            System.out.println("do a 'telnet localhost 2525'
and press enter");
            SocketChannel sc = ssc.accept();
            
            Socket s = sc.socket();
            s.setSoTimeout(socketTimeout);
            
            InputStream is = s.getInputStream();
            is.read();
            
            if (workaround) {
                s.shutdownOutput();
                s.shutdownInput();
            }
            
            is.close();
            s.close();
            sc.close();
            ss.close();
            ssc.close();
            System.out.println("everything closed");
            
            Thread.sleep(99999); // if the VM ends, the
connection is closed
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In real-world applications, you have to surround the
'shutdownIn/Output' with try-catch, as this throws an
exception if the remote side already closed the connection.


Submitted On 02-AUG-2004
Gregorio.Roper
This is still reproducible with java 1.5.0beta2


Submitted On 02-JUL-2005
b_rahul
I still see thi shappening on Linux and JDK 1.5.0-03


Submitted On 17-JUL-2005
Ernimril
why is this bug closed? The problem still exists in 
1.5.0_04 and mustang latest build


Submitted On 25-OCT-2005
iraisr
I confirm this bug is STILL PRESENT in JRE 1.5.0_05 both under WindowsXP and Linux AMD64. However, workaround with socket().shutdownInput and socket().shotdownOutput works - FIN is sent.


Submitted On 07-FEB-2006
curiousmetoo
This should NOT have been closed. We have opened a separate ticket with Sun. A patch needs to be issued for JDK 1.5 and JDk 1.4 as needed.



PLEASE NOTE: JDK6 is formerly known as Project Mustang