Java: nice way to stop threaded TCP server?
Asked Answered
E

3

5

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?

Elianaelianora answered 11/4, 2011 at 15:28 Comment(0)
I
3

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

Idiopathy answered 11/4, 2011 at 15:38 Comment(0)
N
2

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.

Neu answered 11/4, 2011 at 15:35 Comment(3)
Best practice is to use Thread.interrupted(). This is standard solution which will work correctly in ThreadPool.Tremulous
@Andrey Vityuk Thread.interrupted is actually not great practice because it reset the interruption flag. You may have meant Thread.interrupt or Thread.isInterrupted. That being said, as I said in my answer, many IO constructs do not take thread interruption into account so (though I partially agree with your comment) it doesnt hold in this situation.Idiopathy
@Andrey Interrupting a thread that is blocked reading from a socket will not interrupt it. Yhe only way to close the thread is closing the socket and catching the exception SocketException to do some clean ups.Containment
U
2

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
            }
        }
    }
Unitary answered 11/4, 2011 at 15:38 Comment(2)
I understand this very well. "Then you can either wait for the current worker threads to finish their work" - if you read my question again, you will see that this is a problem. Workers can be blocked by reading from socket stream. And the question is how to gracefully interrupt them. Flag will not work here because worker cannot check it while in blocked I/O stateElianaelianora
“You must not simply stop the server.” Actually, if you've got your server design right (ACID transactions and all) just stopping it will work. A change that results in a database commit is deemed to have happened, and a change that isn't, never happened. Making this work well is one of the good reasons for separating the database from the middle-tied business logic and web front end.Assuan

© 2022 - 2024 — McMap. All rights reserved.