19f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes/*
29f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * Copyright (C) 2010 The Android Open Source Project
39f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes *
49f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * Licensed under the Apache License, Version 2.0 (the "License");
59f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * you may not use this file except in compliance with the License.
69f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * You may obtain a copy of the License at
79f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes *
89f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes *      http://www.apache.org/licenses/LICENSE-2.0
99f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes *
109f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * Unless required by applicable law or agreed to in writing, software
119f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * distributed under the License is distributed on an "AS IS" BASIS,
129f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * See the License for the specific language governing permissions and
149f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * limitations under the License.
159f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes */
169f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
174557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonpackage libcore.java.net;
189f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
193d43199950729e592ac3e104442b4b350a86aec8Elliott Hughesimport java.io.IOException;
204557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport java.net.DatagramPacket;
214557728efb66c455a52b7669a8eefef7a9e54854Jesse Wilsonimport java.net.DatagramSocket;
229f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.net.InetSocketAddress;
239f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.net.ServerSocket;
249f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.net.Socket;
253d43199950729e592ac3e104442b4b350a86aec8Elliott Hughesimport java.net.SocketAddress;
269f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.net.SocketException;
279f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.nio.channels.AsynchronousCloseException;
283c6ca77845ce5f0e80c4dc49617601918023c1edElliott Hughesimport java.nio.channels.ClosedChannelException;
299f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.nio.channels.SocketChannel;
309f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.util.ArrayList;
319f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.util.List;
329f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughesimport java.util.concurrent.CopyOnWriteArrayList;
335fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughesimport tests.net.StuckServer;
349f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
359f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes/**
369f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * Test that Socket.close called on another thread interrupts a thread that's blocked doing
379f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes * network I/O.
389f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes */
399f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughespublic class ConcurrentCloseTest extends junit.framework.TestCase {
409f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_accept() throws Exception {
4178601286edbfed7ea072814763f661eb80f6e777Elliott Hughes        ServerSocket ss = new ServerSocket(0);
4278601286edbfed7ea072814763f661eb80f6e777Elliott Hughes        new Killer(ss).start();
439f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        try {
449f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            System.err.println("accept...");
4578601286edbfed7ea072814763f661eb80f6e777Elliott Hughes            Socket s = ss.accept();
4678601286edbfed7ea072814763f661eb80f6e777Elliott Hughes            fail("accept returned " + s + "!");
479f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (SocketException expected) {
489f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            assertEquals("Socket closed", expected.getMessage());
499f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
509f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
519f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
529f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_connect() throws Exception {
530e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes        StuckServer ss = new StuckServer(false);
549f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        Socket s = new Socket();
559f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        new Killer(s).start();
569f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        try {
570e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes            System.err.println("connect...");
585fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughes            s.connect(ss.getLocalSocketAddress());
5978601286edbfed7ea072814763f661eb80f6e777Elliott Hughes            fail("connect returned: " + s + "!");
609f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (SocketException expected) {
61796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes            assertEquals("Socket closed", expected.getMessage());
62796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes        } finally {
63796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes            ss.close();
64796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes        }
65796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes    }
66796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes
67796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes    public void test_connect_timeout() throws Exception {
680e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes        StuckServer ss = new StuckServer(false);
69796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes        Socket s = new Socket();
70796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes        new Killer(s).start();
71796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes        try {
720e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes            System.err.println("connect (with timeout)...");
73796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes            s.connect(ss.getLocalSocketAddress(), 3600 * 1000);
7478601286edbfed7ea072814763f661eb80f6e777Elliott Hughes            fail("connect returned: " + s + "!");
75796f0d5a4e7b83c3efc5e587b6766977dc20b0c3Elliott Hughes        } catch (SocketException expected) {
769f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            assertEquals("Socket closed", expected.getMessage());
775fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughes        } finally {
785fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughes            ss.close();
799f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
809f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
819f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
829f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_connect_nonBlocking() throws Exception {
830e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes        StuckServer ss = new StuckServer(false);
849f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        SocketChannel s = SocketChannel.open();
859f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        new Killer(s.socket()).start();
869f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        try {
870e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes            System.err.println("connect (non-blocking)...");
889f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            s.configureBlocking(false);
895fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughes            s.connect(ss.getLocalSocketAddress());
909f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            while (!s.finishConnect()) {
919f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                // Spin like a mad thing!
929f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            }
9378601286edbfed7ea072814763f661eb80f6e777Elliott Hughes            fail("connect returned: " + s + "!");
949f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (SocketException expected) {
959f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            assertEquals("Socket closed", expected.getMessage());
969f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (AsynchronousCloseException alsoOkay) {
973c6ca77845ce5f0e80c4dc49617601918023c1edElliott Hughes            // See below.
983c6ca77845ce5f0e80c4dc49617601918023c1edElliott Hughes        } catch (ClosedChannelException alsoOkay) {
999f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // For now, I'm assuming that we're happy as long as we get any reasonable exception.
1009f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // It may be that we're supposed to guarantee only one or the other.
1015fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughes        } finally {
1025fc5dde4c719c1dfdac46b67d5d2e4884d07721eElliott Hughes            ss.close();
1039f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1049f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
1059f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
1069f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_read() throws Exception {
1073d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        SilentServer ss = new SilentServer();
1089f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        Socket s = new Socket();
1099f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        s.connect(ss.getLocalSocketAddress());
1109f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        new Killer(s).start();
1119f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        try {
1129f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            System.err.println("read...");
1139f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            int i = s.getInputStream().read();
11478601286edbfed7ea072814763f661eb80f6e777Elliott Hughes            fail("read returned: " + i);
1159f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (SocketException expected) {
1169f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            assertEquals("Socket closed", expected.getMessage());
1179f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1189f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        ss.close();
1199f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
1209f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
1219f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_read_multiple() throws Throwable {
1223d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        SilentServer ss = new SilentServer();
1239f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        final Socket s = new Socket();
1249f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        s.connect(ss.getLocalSocketAddress());
1259f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
1269f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        // We want to test that we unblock *all* the threads blocked on a socket, not just one.
1279f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        // We know the implementation uses the same mechanism for all blocking calls, so we just
1289f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        // test read(2) because it's the easiest to test. (recv(2), for example, is only accessible
1299f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        // from Java via a synchronized method.)
1309f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        final ArrayList<Thread> threads = new ArrayList<Thread>();
1319f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        final List<Throwable> thrownExceptions = new CopyOnWriteArrayList<Throwable>();
1329f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        for (int i = 0; i < 10; ++i) {
1339f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            Thread t = new Thread(new Runnable() {
1349f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                public void run() {
1359f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                    try {
1369f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                        try {
1379f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                            System.err.println("read...");
1389f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                            int i = s.getInputStream().read();
13978601286edbfed7ea072814763f661eb80f6e777Elliott Hughes                            fail("read returned: " + i);
1409f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                        } catch (SocketException expected) {
1419f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                            assertEquals("Socket closed", expected.getMessage());
1429f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                        }
1439f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                    } catch (Throwable ex) {
1449f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                        thrownExceptions.add(ex);
1459f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                    }
1469f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                }
1479f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            });
1489f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            threads.add(t);
1499f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1509f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        for (Thread t : threads) {
1519f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            t.start();
1529f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1539f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        new Killer(s).start();
1549f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        for (Thread t : threads) {
1559f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            t.join();
1569f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1579f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        for (Throwable exception : thrownExceptions) {
1589f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            throw exception;
1599f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1603d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes
1613d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        ss.close();
1629f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
1639f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
1649f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_recv() throws Exception {
1659f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        DatagramSocket s = new DatagramSocket();
1669f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        byte[] buf = new byte[200];
1679f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        DatagramPacket p = new DatagramPacket(buf, 200);
1689f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        new Killer(s).start();
1699f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        try {
1709f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            System.err.println("receive...");
1719f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            s.receive(p);
1729f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            fail("receive returned!");
1739f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (SocketException expected) {
1749f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            assertEquals("Socket closed", expected.getMessage());
1759f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1769f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
1779f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
1789f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    public void test_write() throws Exception {
1793d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        final SilentServer ss = new SilentServer();
1809f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        Socket s = new Socket();
1819f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        s.connect(ss.getLocalSocketAddress());
1829f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        new Killer(s).start();
1839f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        try {
1849f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            System.err.println("write...");
1859f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // We just keep writing here until all the buffers are full and we block,
1869f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // waiting for the server to read (which it never will). If the asynchronous close
1879f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // fails, we'll see a test timeout here.
1889f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            while (true) {
1899f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                byte[] buf = new byte[256*1024];
1909f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                s.getOutputStream().write(buf);
1919f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            }
1929f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        } catch (SocketException expected) {
1939f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // We throw "Connection reset by peer", which I don't _think_ is a problem.
1949f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            // assertEquals("Socket closed", expected.getMessage());
1959f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
1969f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        ss.close();
1979f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
1989f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
1993d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes    // This server accepts connections, but doesn't read or write anything.
2003d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes    // It holds on to the Socket connecting to the client so it won't be GCed.
2013d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes    // Call "close" to close both the server socket and its client connection.
2023d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes    static class SilentServer {
2033d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        private final ServerSocket ss;
2043d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        private Socket client;
2053d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes
2063d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        public SilentServer() throws IOException {
2073d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes            ss = new ServerSocket(0);
2083d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes            new Thread(new Runnable() {
2093d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                public void run() {
2103d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                    try {
2113d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                        client = ss.accept();
2123d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                    } catch (Exception ex) {
2133d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                        ex.printStackTrace();
2143d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                    }
2153d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes                }
2163d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes            }).start();
2173d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        }
2183d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes
2193d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        public SocketAddress getLocalSocketAddress() {
2203d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes            return ss.getLocalSocketAddress();
2213d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        }
2223d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes
2233d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        public void close() throws IOException {
2243d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes            client.close();
2253d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes            ss.close();
2263d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes        }
2273d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes    }
2283d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes
2293d43199950729e592ac3e104442b4b350a86aec8Elliott Hughes    // This thread calls the "close" method on the supplied T after 2s.
2309f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    static class Killer<T> extends Thread {
2319f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        private final T s;
2329f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
2339f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        public Killer(T s) {
2349f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            this.s = s;
2359f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
2369f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes
2379f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        public void run() {
2389f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            try {
2399f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                System.err.println("sleep...");
2409f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                Thread.sleep(2000);
2410e1afa1091f74a6228c01d8c7a8eebf001efdc57Elliott Hughes                System.err.println("close...");
2429f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                s.getClass().getMethod("close").invoke(s);
2439f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            } catch (Exception ex) {
2449f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes                ex.printStackTrace();
2459f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes            }
2469f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes        }
2479f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes    }
2489f2b1b1c3bf9de560f29f257c855c7c85b405c0fElliott Hughes}
249