Java, работа с Socket. Socket#isConnected не изменяется при отключении клиента!

Есть класс для создания сервера и управления подключениями:

public abstract class Server {

    //...
    public void start() {
        new Thread(() -> {

             while(true) {
                try {
                    Socket connection = server.accept();

                    // новый поток для сокета
                    new Thread(new Monitor(connection)).start();

                } catch (IOException e) {
                    System.out.println("Accept filed!");
                }
            }
        }).start();
    }

    private class Monitor implements Runnable {
        Socket socket;
        DataInputStream input;
        StringBuilder message;
        boolean status = true;

        public Monitor(Socket socket) {
            this.socket = socket;
            try {
                input = new DataInputStream(socket.getInputStream());
            } catch (IOException e) {
                status = false;
            }
        }

        @Override
        public void run() {
            while (status) {

                try {
                    // если disconnected
                    if(!socket.isConnected()) break;

                    // если есть новые данные 
                    if (input.available() > 0) {

                        message = new StringBuilder();
                        do {
                            message.append(input.readChar());
                        } while(input.available() > 0);

                        onMessage(socket, message.toString());
                    }

                    Thread.sleep(1000);
                }
                catch(IOException | InterruptedException e) {
                    break;
                }
            }

            onDisconnect(socket);
            connections.remove(socket);
        }
    }
}

Проблема в том, что при отключении клиента socket.isConnected() продолжает остаться true и никакой exception не появляется.
В чем причина??


Ответы (1 шт):

Автор решения: Kostya

available() действительно не бросает ошибок.

Есть 2 способа понять что с сокетом проблемы:

  1. Поймать IOException при чтении
  2. Установить таймаут у сокета и поймать SocketTimeoutException

Для изменения вашего кода нужно разобраться что мы хотим сделать. Вы ожидаете от клиента некоторое сообщение(пакет), но при этом вы не знаете его размер. Вам нужно определиться сколько символов(байт и т.д.) нужно прочитать или что будет служить признаком конца сообщения, для того что бы начать обрабатывать сообщение. В вашем случае для определения конца сообщения можно использовать символ новой строки \n. Например:

public void run() {
    message = new StringBuilder();
    char sym;

    while(status) {
        try {
            sym = input.readChar();

            if(sym == '\n') {
                onMessage(socket, message.toString());
                message = new StringBuilder();
            } else {
                message.append(sym);
            }
        } catch (IOException e) {
            status = false;
        }
    }
}

И добавить таймаут соединения:

this.socket = socket;
socket.setSoTimeout(10_000);
→ Ссылка