Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: nice way to stop threaded TCP server?

I have the following structure for TCP client-server communication:

  • On server startup server starts acceptor thread, that accepts client connections and passes ServerSocket to it.
  • When a client connection arrives, acceptor thread calls accept() on ServerSocket and submits client processing job to worker thread (by executor/thread pool) and provides client socket to it.
  • Worker in loop reads data from client socket stream, processes it and sends replies.

The question is how to gracefully stop the whole system? I can stop acceptor thread by just closing ServerSocket. It will cause accept() blocking call to throw SocketException. But how to stop workers? They read from stream and this call is blocking. According to this streams does not throw InterruptedException and thus worker cannot be interrupt()'ed.

It looks like I need to close worker socket from another thread, right? For this, socket should be made a public field or a method should be provided in worker to close it. Will this be nice? Or may be my whole design is flawed?

like image 401
Vladislav Rastrusny Avatar asked Apr 11 '11 15:04

Vladislav Rastrusny


3 Answers

Your model works appropriately. The best way of interrupting non-interruptable constructs like IO is to close the socket. You can of course handle it before you go into a blocking state, but if the IO functions don't react to interruption you dont really have many good options

like image 141
John Vint Avatar answered Sep 23 '22 13:09

John Vint


I would suggest using a boolean flag that the workers check periodically. Call the flag shouldStop and if it's set to true, the worker cleans up and then dies. Following this method would allow you to implement some clean-up code so you don't leave resources hanging, etc.

like image 44
Chris Thompson Avatar answered Sep 23 '22 13:09

Chris Thompson


You must not simply stop the server. The shutdown process might take a while while cleanup occurs because you need to ensure consistency.

Imagine a database server, if you simply shut it down while it is carrying out transactions you may leave its data inconsistent. That's why it typically takes a while to shutdown the server.

  • You must first stop accepting new connections in the server.
  • Then you can either wait for the current worker threads to finish their work and then close the server and shutdown officially.
  • Or you force the worker threads to close their connections with the
    client (probably using some sort
    of flag as suggested). This might imply some cleanup to leave data consistent, for instance revert trasnsactions or any kind of changes you have done in files or in memory.

Closing the connections with the clients in the server side should cause the clients to get a EOF on their sides as far as I understand.

[EDIT-1]

I have delved a bit on the issue, just because I had not used sockets in a while and because I found the question interesting. I think as it has been well pointed out by others, the only option is to close the socket, which according got Javadocs will automatically close the input and output streams/

If there are chances that the thread is not IO-blocked, but in wait state or sleeping, I think it is still recommended to issue a Thread.interrupt() for the corresponding worker thread of a given socket; because there cannot be certainty of the blocking of state of every thread.

public static class IOServerWorker implements Runnable{

        private Socket socket;

        public IOServerWorker(Socket socket){
            this.socket = socket;
        }

        @Override
        public void run() {
            String line = null;
            try{
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while( (line = reader.readLine())!=null){
                    System.out.println(line);
                }
                reader.close();
            }catch(IOException e){
                //TODO: do cleanup here
                //TODO: log | wrap | rethrow exception
            }
        }
    }
like image 28
Edwin Dalorzo Avatar answered Sep 22 '22 13:09

Edwin Dalorzo