Почему InputStream.read() генерирует исключение «Время ожидания чтения истекло», если объект inputStream ранее был заключен в BufferedReader?

У меня есть объект inputStream, который поступает из сокета.

Этот поток может быть полностью текстовым или содержать двоичные данные. У него есть простой текстовый «префикс», который сообщает мне, какой тип данных у него есть. Я использую BufferedReader для чтения этого префикса. Затем, если тип данных — обычный текст, я продолжаю использовать BufferedReader для чтения остальных данных, которые прекрасно работают.

Проблема в том, что когда я снова переключаюсь на использование InputStream, если префикс указывает нетекстовые данные. Стрим как будто где-то потерялся, а вместо этого я получаю java.net.SocketTimeoutException: Read timed out.

Код выглядит примерно так:

InputStream instream = mysocket.getInputStream();
BufferedReader br = (new InputStreamReader(instream, "UTF-8")));

byte[] prefixArray = new byte[4];
br.read(prefixArray, 0, 4);
String prefix = new String(prefixArray);

if ("text".equals(prefix)) {
   // continue to use br.read() here, which works fine...
}else{
   byte[] barray = new byte[arraysize]
   instream.read(barray, 0, arraysize); // THROWS "Read timed out" exception
}

Это просто не разрешено? Как только instream завернут в BufferedReader, его больше нельзя будет использовать напрямую?

Буферизованный ридер, как следует из его названия и поясняется в его javadoc, буферизует. Когда вы попросите его прочитать два символа, он прочитает, скажем, 4096 и сохранит их в буфере, чтобы ускорить чтение в будущем. Поэтому, если вы вернетесь к обернутому потоку для чтения, вы прочитаете байты, расположенные после байтов первых 4096 символов.

JB Nizet 10.07.2019 23:44

@JBNizet, это имеет смысл. Не стесняйтесь сделать свой комментарий ответом, чтобы я мог его принять. Спасибо!

Zoomzoom 10.07.2019 23:47

@JamesKPolk спасибо. Я задавался вопросом, был ли это правильный английский. "буферы" - правильный глагол?

JB Nizet 11.07.2019 08:38

@JBNizet: Да, buffers правильно, но в некотором смысле bufferizes более выразительно, если не совсем правильно. Это могло бы сделать лучшую работу, чтобы сделать вашу точку зрения.

President James K. Polk 11.07.2019 13:52
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
4
730
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Время чтения вашего двоичного файла истекло, потому что BufferedReader уже прочитал байты, которые вы пытаетесь прочитать.

Вам потребуется всего 4 байта, чтобы определить тип потока. Использование Reader для этих 4 байтов является излишним. Сначала прочитайте байты непосредственно из InputStream, затем создайте Reader только для text данных, например:

InputStream instream = mysocket.getInputStream();

byte[] prefixArray = new byte[4];
int offset = 0;
int numread;
do
{
    numread = instream.read(prefixArray, offset, 4-offset);
    if (numread == -1) return;
    offset += numread;
}
while (offset < 4);
String prefix = new String(prefixArray);

if ("text".equals(prefix))
{
    BufferedReader br = new BufferedReader(new InputStreamReader(instream, "UTF-8"));
    //...
}
else
{
    byte[] barray = new byte[arraysize];
    // consider using BufferedInputStream here ...
    do
    {
        numread = instream.read(barray, 0, arraysize);
        if (numread == -1) break;
        //...
    }
    while (true);
}

В качестве альтернативы рассмотрите возможность использования BufferedInputStream для чтения сокета и добавьте другие классы поверх него по мере необходимости, например:

InputStream instream = mysocket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(instream);

byte[] prefixArray = new byte[4];
DataInputStream dis = new DataInputStream(bis);
dis.readFully(prefixArray);
String prefix = new String(prefixArray);

if ("text".equals(prefix))
{
    InputStreamReader isr = new InputStreamReader(bis, "UTF-8"));
    //...
}
else
{
    byte[] barray = new byte[arraysize];
    do
    {
        numread = bis.read(barray, 0, arraysize);
        if (numread == -1) break;
        //...
    }
    while (true);
}

Другие вопросы по теме