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