Как мне читать из буферизованного ридера?

У меня есть следующий пример чтения из буферизованного ридера:

while ((inputLine = input.readLine()) != null) {
   System.out.println("I got a message from a client: " + inputLine);
}

Код в цикле println будет выполняться всякий раз, когда что-то появляется в буферизованном считывателе (в данном случае input). В моем случае, если клиент-приложение что-то напишет в сокет, код в цикле (в сервере-приложении) будет выполнен.

Но я не понимаю, как это работает. inputLine = input.readLine() ждет, пока что-то появится в буферизованном считывателе, и когда там что-то появится, возвращает true и выполняется код в цикле. Но когда null можно вернуть.

Есть еще вопрос. Приведенный выше код был взят из метода, который throws Exception и я использую в методе запуска потока. И когда я пытаюсь поставить throws Exception перед run, компилятор жалуется: переопределенный метод не генерирует исключение. Без throws exception у меня есть еще одна жалоба от компилятора: незарегистрированное исключение. Так что я могу сделать?


person Roman    schedule 23.03.2010    source источник
comment
@ Роман, ты знаешь, в чем дело. Ставьте разные вопросы в разные вопросы SO!!   -  person Stephen C    schedule 23.03.2010
comment
Стивен С, я думал, что добавленная часть также является частью того, как мне читать из буферизованного ридера? Но я вижу вашу точку зрения. Я удалил добавленную часть.   -  person Roman    schedule 23.03.2010


Ответы (9)


Когда сокет на другом конце закрыт, считыватель должен вернуть нулевую строку. Это то состояние, которое вы ищете. Чтобы обработать исключение, оберните цикл чтения в блок try/catch.

 try {
   while ((inputLine = input.readLine()) != null) {
     System.out.println("I got a message from a client: " + inputLine);
   }
 }
 catch (IOException e) {
   System.err.println("Error: " + e);
 }

Вы можете найти это руководство по чтению/записи из/ к сокету в Java, полезно.

person tvanfosson    schedule 23.03.2010

По первому вопросу:

Но я не понимаю, как это работает. inputLine = input.readLine() ждет, пока что-то появится в буферизованном считывателе, и когда там что-то появится, он возвращает true, и код в цикле выполняется. Но когда null может быть возвращен.

BufferedReader.readLine() делает не возвращать true в случае успеха. Он возвращает строку, содержащую прочитанную строку. Если достигнут конец потока, он возвращает null.

Ваш второй вопрос:

Приведенный выше код был взят из метода, который выдает исключение, и я использую этот код в методе запуска потока. И когда я пытаюсь поставить Exception перед запуском, компилятор жалуется: переопределенный метод не кидает исключение. Без исключения throws у меня есть еще одна жалоба от компилятора: незарегистрированное исключение. Так что я могу сделать?

Вы должны поместить свой код в блок try/catch. Если вы не хотите обрабатывать перехваченное исключение, просто оставьте эту часть пустой (не рекомендуется).

try {
    while ((inputLine = input.readLine()) != null) {
        System.out.println("I got a message from a client: " + inputLine);
    }
} catch (Exception e) {
    //handle exception
}
person Peter Di Cecco    schedule 23.03.2010

ReadLine() считывателя вернет строковое значение, когда что-то будет прочитано, пустую строку, если еще ничего не было, и null, когда соединение будет закрыто.

Я бы рекомендовал обернуть try/catch вокруг вашего блока кода с помощью функции ввода-вывода и соответствующим образом обрабатывать ошибки.

person Bryan Denny    schedule 23.03.2010

input считыватель подключен к сокету, который является слушателем, т.е. продолжает прослушивать входящие сообщения.

Что касается вашего второго вопроса, вы должны поместить блок try/catch внутри метода, поймать исключение и обработать его. Не бросайте повторно.

person medopal    schedule 23.03.2010

Но я не понимаю, как это работает. .... ждет, пока что-то появится в буферизованном ридере, и когда там что-то появится, он возвращает true

Нет, он возвращает значение выражения (inputLine = input.readLine()), саму inputLine. inputLine сравнивается с нулем.

person PeterMmm    schedule 23.03.2010

null возвращается, когда достигается «EOF (конец файла)». Поскольку это чтение из сетевого сокета, конец файла создается при отключении сокета (либо сервером, либо клиентом), но вы, скорее всего, получите исключение до того, как действительно увидите EOF.

person M. Jessup    schedule 23.03.2010

Если это не домашнее задание, вы можете посмотреть Apache Commons IOtils.

Предполагая, что вы не создаете BufferedReader и просто останавливаетесь на InputStream:

String results = IOUtils.toString(inputStream);
System.out.println(results);
person Dean J    schedule 23.03.2010

while ((inputLine = input.readLine()) != null) {

Посмотрите на каждую часть выражения:

input.readLine()

Возвращает строку, которая будет нулевой, если достигнут конец потока (или генерирует исключение при ошибке).

inputLine = input.readLine()

Назначает эту строку для inputLine

((inputLine = input.readLine()) != null)

Проверяет, что назначенная строка не является нулевой (конец потока).

person fgb    schedule 23.03.2010

Вы получили хорошие ответы. Просто поймайте исключение и обработайте его локально. Если вам нужно передать это другому коду, но не можете, поскольку метод run() не допускает никаких исключений проверки, вы можете обернуть исключение в какое-либо исключение RuntimeException. Если метод запуска выполняется непосредственно в потоке (поскольку, вероятно, это Runnable), вам следует позаботиться о повторном создании завернутого исключения.

Что касается результата от readLine(), он вернет null, когда больше нечего читать. В случае сокета это когда другая сторона полностью закрывает сокет (любое внезапное завершение или нечистое закрытие обычно приводит к исключению в вашем коде, поскольку ОС будет отправлять уведомление о закрытии сокета другого типа).

У меня есть одно предостережение, поскольку вы заключаете сокет в java.io.BufferedReader. Вы должны быть очень осторожны при использовании этого в любом производственном коде.

Опасность заключается в том, что BufferedReader плохо справляется с исключениями во время чтения. Это особенно проблема, если вы включили тайм-аут для сокета, поэтому код будет автоматически получать периодические исключения из операционной системы. Тайм-аут (или другое исключение) может произойти во время заполнения буфера внутри считывателя. Если вы попытаетесь повторно использовать объект после исключения, он проигнорирует все предыдущее содержимое в буфере. Пакет(ы), которые были получены ранее, теряются без уведомления, и нет никакого способа получить эти байты.

Обратите внимание, что существуют другие виды исключений сокетов, которые не означают, что сокет был потерян. Например, посмотрите на определение java.io.InterruptedIOException. У этого есть общедоступная переменная, которая сообщает о количестве байтов, успешно переданных в самом последнем запросе ввода-вывода (чтение или запись). Это означает, что операция ввода-вывода может быть выполнена снова, чтобы получить или отправить оставшиеся байты для пакета.

Если при каком-либо исключении ваш дизайн заключается в немедленном закрытии считывателя и сокета, метод будет работать правильно.

Правильный способ чтения из сокета — использовать поток сокета напрямую, использовать NIO (ByteBuffers и тому подобное) или использовать хорошо написанную сетевую библиотеку с хорошими абстракциями над этими классами более низкого уровня (доступно несколько с открытым исходным кодом).

person Kevin Brock    schedule 23.03.2010