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

Socket descriptors leak into processes spawned by Java programs on windows

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • 1.1.7
    • 1.1, 1.1.7, 1.2.0, 1.2.2
    • core-libs
    • b04
    • x86
    • windows_nt

        On windows NT if you open a server socket and then spawn a sub-process
        the child inherits the the socket, this will of course keep another
        process from using the port associated with the socket.

        This problem is compounded by a windows "feature" where if the parent
        process goes away and a child who has inherited a socket descriptor is
        still around clients who try to connect to the parent will succeeded,
        (if they try to read they will block until the child dies and
        any writes go to the big bit bucket in the sky)

        I have included simple Java programs that demonstrate this bug. Run
        TCPServer with something like
        java TCPServer open exec "notepad" exit

        Then try connecting to it with TCPClient
        java TCPClient <host TCPServer was on> <port> <string of text>

        If notepad is still around the client will run normally, if notepad is killed the client will generate an exception

        You can use the sleep argument with TCPServer to get to hang around before
        exiting, this allows you to make sure TCPClient can connect succefully
        when TCPServer is still running.

        Sample code:

        import java.net.*;
        import java.io.*;

        public class TCPServer {
            static private String svrName;

            static private class AcceptThread extends Thread {
        private ServerSocket sSock;

        AcceptThread(ServerSocket sSock) {
        this.sSock = sSock;
        }

        public void run() {
        try {
        while (true) {
        final Socket sock = sSock.accept();
        (new ReadThread(sock)).start();
        }
        } catch (IOException e) {
        System.err.println(svrName +
        ":IOException opening server socket");
        e.printStackTrace();
        }
        }
            }

            static private class ReadThread extends Thread {
        private BufferedReader rdr;

        ReadThread(Socket sock) throws IOException {
        rdr = new BufferedReader(
        new InputStreamReader(sock.getInputStream()));
        }

        public void run() {
        try {
        while (true) {
        final String str = rdr.readLine();
        if (str == null) {
        System.out.println(svrName +
        ":EOF recived, thread exiting");
        return;
        } else if (str.equals("bye")) {
        System.out.println(svrName + ":Recived bye, exiting");
        System.exit(0);
        } else {
        System.out.println(svrName + ":" + str);
        }
        }
        } catch (IOException e) {
        System.err.println(svrName + ":IOException in read thread");
        e.printStackTrace();
        }
        }
            }

            /** Utility class to forward bytes from one stream to another */
            static private class Dumper extends Thread {
        private final InputStream in;
        private final OutputStream out;
        private final boolean flush;

        /**
        * Create a new <code>Dumper</code>
        * @param in The stream to take bytes from
        * @param out The stream to dump the bytes to
        * @param flust If true flush out after each write.
        */
        Dumper(InputStream in, OutputStream out, String name) {
        super(name);
        this.in = in;
        this.out = out;
        this.flush = true;
        }

        /**
        * Read a bunch of bytes from the associated
          * <code>InputStream</code> and write them to the associated
        * <code>OutputStream<code>, repeat until the read returns
        * EOF, or until there is an <code>IOException</code>
        */
        public void run() {
        byte[] buf = new byte[4096];
        try {
        while (true) {
        final int bytesRead = in.read(buf);
        if (bytesRead > 0) {
        out.write(buf, 0, bytesRead);
        if (flush) out.flush();
        } else if (bytesRead < 0)
        break;
        }
        } catch (IOException e) {
        System.err.println(svrName + ":IOException in dump thread");
        e.printStackTrace();
        } finally {
        try {
        in.close();
        } catch (IOException e) {
        // Just trying to be nice
        }
        }
        }
            }

            public static void main(String[] args) throws IOException {
        svrName = args[0];

        for (int i=1; i<args.length; i++) {
        final String arg = args[i];

        if (arg.equals("open")) {
        final ServerSocket sSock = new ServerSocket(0);
        (new AcceptThread(sSock)).start();
        System.out.println(svrName +
        ":Opened Accept Socket on port " +
        sSock.getLocalPort());
        } else if (arg.equals("exec")) {
        final String execStr = args[++i];
        final Runtime rt = Runtime.getRuntime();
        final Process proc = rt.exec(execStr);

        Thread outThread =
        new Dumper(proc.getInputStream(), System.out,
        "Out Forwarding Thread");

        Thread errThread =
        new Dumper(proc.getErrorStream(), System.err,
        "Err Forwarding Thread");

        outThread.start();
        errThread.start();

        System.out.println(svrName + ":Execed:" + execStr);
        } else if (arg.equals("sleep")) {
        final String sleepTimeStr = args[++i];
        final long sleepTime = Long.parseLong(sleepTimeStr);
        try {
        System.out.print(svrName + ":Sleeping for " + sleepTime +
        "milliseconds...");
        System.out.flush();
        Thread.sleep(sleepTime);
        System.out.println("awake");
        } catch (InterruptedException e) {
        break;
        }
        } else if (arg.equals("exit")) {
        System.out.println(svrName + ":Exiting...");
        System.exit(0);
        }
        }
            }
        }



        import java.net.*;
        import java.io.*;

        public class TCPClient {
            public static void main(String[] args) throws IOException {
        final int port = Integer.parseInt(args[1]);
        final Socket sock = new Socket(args[0], port);

        final PrintWriter out = new PrintWriter(sock.getOutputStream(), true);

        out.println(args[2]);
            }
        }

        ---------------------------------------------------------------------------------------------------
        Another of our licensees has also reported this problem :

        There are two aspects to this - inheritance for plain sockets, and for
        datagram sockets. There is a test case for each of these and the fix is
        similar for both of them.
        Both the fixes are similar, and are in the same file. I have included the
        diffs below for the 1.1.7 level code

        >>Plain Sockets

        Test Case

         TCPClient.java TCPServer.java

        Two files TCPServer and TCPClient. These come from Sunbug 4197666 which is
        also for plain socket inheritance (not datagram socket inheritance). To
        run the test case - you can use one machine
        In a server "window" - run
             java TCPServer localHost open exec "notepad" exit.
        This will start the server, run the notepad and exit the server.

        Run the client
             java TCPClient <machine ip address> <port of server> sometextgoeshere

        The client will run and exit, even though the server has exited! Try
        running the whole server and client again, but BEFORE running the client,
        close notepad. The client will be unable to connect to the server. The
        fix below will cure the problem

        (See TCPClient.java and TCPServer.java above)


        >>Datagram Sockets

        Test Case - DatagramClient Datagramserver

        Start the server and then start the client. No command line parameters
        required - uses the local host machine. About 10 communications will flow
        between the client and sever. On communication 5, notepad will be
        started. The problem will manifest it self in that a second server can not
        be created if notepad is still active. Ignore the fact that the client
        hangs - as this is udp it is waiting for a response that will never come!
        The important part is that the server can be restarted. Closing notepad
        will allow a new server to start - or implementing the fix below!

        (See DatagramClient.java and DatagramServer.java below)


        ------------------------------------------------------------------------------

        import java.net.*;

        /**
         * This program sends a datagram to the server every 2 seconds and waits
         * for a reply. If the datagram gets lost, this program will hang since it
         * has no retry logic.
         */

        public class DatagramClient extends Object
        {
            public static void main(String[] args)
            {
                try
                {
        // Create the socket for sending
                    DatagramSocket mysock = new DatagramSocket();


        // Create the send buffer
                    byte[] buf = new byte[1024];

        // Create a packet to send. Currently just tries to send to the local host.
        // Change the inet address to make it send somewhere else.

                    DatagramPacket p = new DatagramPacket(buf,
                                                          buf.length, InetAddress.getLocalHost(), 5432);
                    while (true)
                    {
        // Send the datagram
                        mysock.send(p);
                        System.out.println("Client sent datagram!");
        // Wait for a reply
                        mysock.receive(p);
                        System.out.println("Client received datagram!");
                        Thread.sleep(2000);
                    }
                } catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }

        import java.net.*;

        /**
         * This is a simple datagram echo server that receives datagrams
         * and echoes them back untouched.
         */

        public class DatagramServer extends Object
        {
            public static void main(String[] args)
            {
                int counter=1;

                try
                {
                    System.out.println("Starting datagram server");
        // Create the datagram socket with a specific port number
                    DatagramSocket mysock = new DatagramSocket(5432);

        // Allow packets up to 1024 bytes long
                    byte[] buf = new byte[1024];

        // Create the packet for receiving datagrams
                    DatagramPacket p = new DatagramPacket(buf,
                                                          buf.length);
                    while (true)
                    {
        // Read in the datagram
                        mysock.receive(p);

                        System.out.println("Received datagram :"+counter);
                        counter++;




        // A nice feature of datagram packets is that there is only one
        // address field. The incoming address and outgoing address are
        // really the same address. This means that when you receive
        // a datagram, if you want to send it back to the originating
        // address, you can just invoke send again.

                        mysock.send(p);

                        // needs to be after the resend back to the client
                        // otherwise the client will just wait for a message that is never
                        // coming - and that is not hte point of this exercise
                        if (counter==5)
                        {
                           // start an external process
                           Runtime.getRuntime().exec("notepad");
                           System.out.println("Started external process");
                        } else if (counter == 10)
                        {
                            System.out.println("Server exiting");
                            System.exit(0);
                        }

                    }
                } catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }

        mick.fleming@Ireland 1999-04-09

              hongzh Hong Zhang
              duke J. Duke
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: