1
votes

I'm fairly new to sockets and using threads in this way.

I am running into a problem with a scanner waiting for user input and an input stream reader waiting for socket communication at the same time. My program is trying to communicate with a client/server system, and that works fine, however I'm also wanting to be able to input commands into the console directly via a scanner or something similar. However the main thread's while loop is blocking for socket communication, while the scanner is blocking in the inputThread's while loop.

My problem is that if I send the command in the console to close the server (sets the bool 'running' to false), the main thread's while loop still waits for socket communication input. Once it receives any message it'll escape from the while loop due to the bool 'running' being set to false, but only once any message is sent due to it waiting for one before checking the while's conditional.

My other problem is basically the same concept, but inside the inputThread's while loop. If the main thread's while loop breaks then the input thread still has the scanner blocking until it receives user input. Once it receives any user input it'll escape from the while loop due to the thread being interrupted (while loop's conditional).

So in order for my program to exit I have to send the "restart server" message via sockets and user input, when I'd like to send it either way for the program to correctly exit.

How would I solve this problem? I'd assume by cancelling the scanner's blocking when I receive the socket to end the server, but how would I do that? I feel like there's a much better way to do this, any ideas?

Code:

inputThread = new Thread(new Runnable() {
    @Override
    public void run() {
        Logger.log("Starting input thread...");
        while(!Thread.currentThread().isInterrupted()) {
            try {
                scanner = new Scanner(System.in);
                String reply = runCommand(scanner.nextLine());
                Logger.log(reply);
                if(reply.equals("server restarted")) {
                    // TODO: Cancel socket input blocking?
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
        Logger.log("Closing input thread...");
    }
});
inputThread.start();
ServerSocket socket = null;
InputStreamReader inputStream = null;
BufferedReader input = null;
try {
    int port = getPort();
    socket = new ServerSocket(port);
    Logger.log("Server running on port " + port);
    while(running) {
        connection = socket.accept();
        inputStream = new InputStreamReader(connection.getInputStream());
        input = new BufferedReader(inputStream);
        String reply = runCommand(input.readLine());
        if(reply.equals("server restarted")) {
            // TODO: Cancel scanner input blocking?
        }
        reply(reply);
    }
} catch(Exception e) {
    e.printStackTrace();
} finally {
    if(socket != null) {
        try {
            socket.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
    if(connection != null) {
        try {
            connection.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
    if(inputStream != null) {
        try {
            inputStream.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
    if(input != null) {
        try {
            input.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
    if(response != null) {
        try {
            response.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}
inputThread.interrupt();

Thank you for reading

1
In inputThread you check for interrupted state and in main you interrupt, so far so good. But: You surrounded the blocking call on the scanner with try/catch. So if the thread is interrupted, the InterruptedException will be caught, resetting the interrupted state. So you should add a catch block for InterruptedException (before! Exception) and set the interrupted state of the inputThread again. Also you should not only check for interruption, but have an additional condition to check, because spurious interrupts could occur. - Fildor
Correction: Actually it is the throwing of the InterruptedException that resets the interrupted state ... - Fildor
Another possibility would be to inject the Scanner instance to the Thread and close it in main (which will cause an Exception at the blocking nextLine-call that you can catch). All my suggestions assume that you are not willing to switch to NIO, by the way. That would need a major refactoring of that application. - Fildor

1 Answers

0
votes

The first problem is the ServerSocket::acceptcall. It is blocking, but not responsive to interruption (if it were it would be declared to throw InterruptedException). The way to unblock it immediately is to close the socket from the other thread. The accept() call will then immediately throw a SocketException.

The second problem is actually simpler to solve : the while loop should also be checking the running flag. So when a command line input is given to stop the program, the input thread doesn't start waiting for the next command.