Newtron
Newtron

Reputation: 426

Java/Android: Socket closed when offloading work to a thread pool

I'm experiencing a very puzzling error writing a thread-pooled TCP server on Android. Basically, my code is structured as follows:

  1. Standard server loop (blocking call to socket.accept() in a loop within its own thread), calling a handler upon an incoming connection:

    socket = mServerSocket.accept();
    myHandler.onIncomingConnection(socket);
    
  2. The handler offloads all further processing of the incoming connection(s) to a thread pool:

    public class X {
        private final ExecutorService receiveThreadPool = Executors.newSingleThreadExecutor();
    
        [...]
    
        private final ConnectionHandler mHandler = new MyServer.ServerHandler() {
          @Override
          public void onIncomingConnection(final Socket socket) {
    
                      MLog.vv(TAG, "Socket status: " + (socket.isBound() ? "bound" : "unbound") + ", "
                                   + (socket.isConnected() ? "connected" : "unconnected") + ", "
                                   + (socket.isClosed() ? "closed" : "not closed") + ", "
                                   + (socket.isInputShutdown() ? "input shutdown" : "input not shut down") + ".");
    
                      // Process result
                      receiveThreadPool.execute(new Runnable() {
                          @Override
                          public void run() {
                              MLog.vv(TAG, "Socket status: " + 
                                            (socket.isBound() ? "bound" : "unbound") + ... ); // same code as above
    
                              BufferedOutputStream out = null;
    
                              try {
                                  out = new BufferedOutputStream(socket.getOutputStream());
                                  out.write(HELLO_MESSAGE);
                                  out.flush();
    
                                  [rest omitted...]
    
                              } catch (IOException e) {
                                  [...]
                              } finally {
                                  [close resources...]
                              }
    
         }
    

Note, that the socket is defined as final in the handler method's signature, making it accessible from within the anonymous inner Runnable class. However, the first write out.write(HELLO_MESSAGE); to the output stream fails due to a closed socket exception. logcat output:

02-16 17:49:26.383  14000-14057/mypackage:remote V/ManagementServer﹕ Incoming connection from /192.168.8.33:47764
02-16 17:49:26.383  14000-14057/mypackage:remote V/ManagementServer﹕ Socket status: bound, connected, not closed, input not shut down.
02-16 17:49:26.393  14000-14077/mypackage:remote V/ManagementServer﹕ Socket status: bound, unconnected, closed, input not shut down.
02-16 17:49:26.398  14000-14077/mypackage:remote E/ManagementServer﹕ Error communicating with client:
    java.net.SocketException: Socket is closed
            at java.net.Socket.checkOpenAndCreate(Socket.java:665)
            at java.net.Socket.getInputStream(Socket.java:359)
            at  net.semeion.tusynctest.network.ManagementServer$1$1.run(ManagementServer.java:79)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)

As shown in the log output, somehow the socket changes its status from connected to unconnected/closed right after offloading the Runnable into the thread pool. If I just remove the ThreadPool.execute lines, everything works as expected. I have also tried to create my own static Runnable class within the outer class X, passing the socket as a parameter to the Runnable's constructor. However, this triggers the same problem.

Am I missing something here? In theory, this should work like a charm; but somehow it seems, that upon starting the thread and terminating the handler's incomingConnection method, something happens to the socket instance.

Further facts:

Thanks for any hints :)

Upvotes: 2

Views: 986

Answers (1)

Newtron
Newtron

Reputation: 426

I've found the problem now. Embarrassingly, I've overlooked the most obvious source for trouble, the server loop in my Server class:

new Thread(new Runnable() {
    @Override
    public void run() {
      while (mReceiving) {

        Socket recSocket = null;

        try {
            recSocket = mServerSocket.accept();

            // Process connection
            mTcpServerHandler.onIncomingConnection(recSocket);

        } catch (IOException e) {
                // ...
        } finally {
            if (recSocket != null) {
                try {
                    recSocket.close();
                } catch (IOException e) {
                    // log, ignore...
                }
            }
        }
     }
   }
 }).start();

So, in case the handler offloads the processing to a new thread, the method returns immediately, and the finally block of the server loop closes the socket (which was intended originally, but doesn't fit the thread pool approach). Too obvious, sorry for bothering :)

Upvotes: 4

Related Questions