そもそもAndroidOS2.2以前では別ThreadからのSocket#close()が効かない件
http://d.hatena.ne.jp/komamitsu/20111103/1320338412 の続きで少し見てみたら、HttpRequestBase#abort() -> AbstractClientConnAdapter#abortConnection() -> SocketHttpClientConnection#shutdown() -> Socket#close() と来ているので、やっぱりこの辺かなぁと。
今度はこんなのを作って確かめてみました。
public class SocketAbortAndroidActivity extends Activity { private static final String TAG = SocketAbortAndroidActivity.class.getSimpleName(); private final String HOSTNAME = "10.0.2.2"; private final int PORT = 8080; private Socket socket; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); try { socket = new Socket(HOSTNAME, PORT); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } ExecutorService es = Executors.newSingleThreadExecutor(); es.execute(new Runnable() { @Override public void run() { try { OutputStream output = socket.getOutputStream(); output.write("Hello, World!".getBytes()); InputStream input = socket.getInputStream(); byte[] buf = new byte[256]; input.read(buf); Log.d(TAG, "received => " + buf); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { Log.d(TAG, "Finish"); } } }); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) {} try { socket.close(); } catch (IOException e) { e.printStackTrace(); } Log.i(TAG, "Close"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) {} es.shutdown(); } }
OS2.3.3のEmulatorではclose()されると前回同様以下のようにExceptionが発生するのですが、
I/SocketAbortAndroidActivity( 357): Close W/System.err( 357): java.net.SocketException: Socket closed W/System.err( 357): at org.apache.harmony.luni.platform.OSNetworkSystem.read(Native Method) W/System.err( 357): at dalvik.system.BlockGuard$WrappedNetworkSystem.read(BlockGuard.java:273) W/System.err( 357): at org.apache.harmony.luni.net.PlainSocketImpl.read(PlainSocketImpl.java:458) W/System.err( 357): at org.apache.harmony.luni.net.SocketInputStream.read(SocketInputStream.java:85) W/System.err( 357): at org.apache.harmony.luni.net.SocketInputStream.read(SocketInputStream.java:65) W/System.err( 357): at com.komamitsu.SocketAbortAndroidActivity$1.run(SocketAbortAndroidActivity.java:43) W/System.err( 357): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088) W/System.err( 357): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) W/System.err( 357): at java.lang.Thread.run(Thread.java:1019) D/SocketAbortAndroidActivity( 357): Finish
OS2.2ではException発生せず... というかTCPレベルでFINが飛んでないす。
# tcpdump -n tcp port 8080 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes 00:36:00.607484 IP 10.0.2.15.51333 > 10.0.2.2.8080: S 3496404316:3496404316(0) win 5840 <mss 1460,sackOK,timestamp 128072 0,nop,wscale 1> 00:36:00.608027 IP 10.0.2.2.8080 > 10.0.2.15.51333: S 203136001:203136001(0) ack 3496404317 win 8192 <mss 1460> 00:36:00.608117 IP 10.0.2.15.51333 > 10.0.2.2.8080: . ack 1 win 5840 00:36:00.637904 IP 10.0.2.15.51333 > 10.0.2.2.8080: P 1:14(13) ack 1 win 5840 00:36:00.638026 IP 10.0.2.2.8080 > 10.0.2.15.51333: . ack 14 win 8760 (ずっとこのまま...)
明日(というか今日か...)、子供の遊び相手の隙をついてちゃんとAndroidのソースコードを見たいところ...
追記 2011-11-06 22:54
まぁ、結構どうでも良いのだけども、2.2のばあい、
org.apache.harmony.luni.net.PlainSocketImpl#connect() -> org.apache.harmony.luni.platform.OSNetworkSystem#connectWithTimeoutSocketImpl() -> (native) osNetworkSystem_connectWithTimeoutSocketImpl() -> (native) sockConnectWithTimeout() -> (native) doConnect() -> connect()
とconnect(2)を呼んでいるんだけども、その直前で
/* set the socket to non-blocking */ int block = JNI_TRUE; rc = ioctl(handle, FIONBIO, &block);
とsocketをnon-blockingにしている。
2.3のほうは、この辺り (libcore/luni/src/main/native) のコードの構成がガラリと変わっているんだけども結局は
if (!blocking) { flags |= O_NONBLOCK; } else { flags &= ~O_NONBLOCK; } int rc = fcntl(fd, F_SETFL, flags);
とかやってる(non-blockingにしている)。