Подтвердить что ты не робот

Чтение блокировки ввода InputStream Java

Согласно java api, InputStream.read() описывается как:

Если байт недоступен, поскольку конец потока достигнут, возвращается значение -1. Этот метод блоков до тех пор, пока не будут доступны входные данные, конец потока обнаружен или генерируется исключение.

У меня есть цикл while(true), который выполняет чтение, и я всегда получаю -1, когда ничего не передается по потоку. Это ожидалось.

Мой вопрос в том, когда будет читать() когда-либо блокировать? Поскольку, если он не получает никаких данных, он возвращает -1. Я ожидаю, что блокировка чтения будет ждать, пока не будут получены данные. Если вы достигли конца входного потока, не должны читать() просто ждать данных вместо того, чтобы возвращать -1?

Или read() только блокирует, если другой поток, обращающийся к потоку, и ваш read() не может получить доступ к потоку?


Это приводит меня к следующему вопросу. Раньше у меня был прослушиватель событий (предоставленный моей библиотекой), который уведомлял бы меня, когда доступны данные. Когда я был уведомлен, я бы назвал while((aByte = read()) > -1) хранить байт. Я был озадачен, когда я получал ДВЕ события в непосредственной близости от времени, и не все мои данные отображались. Казалось, что только хвост второго события будет отображаться, а остальная часть отсутствует.

В конце концов я изменил свой код, так что, когда я получаю событие, я назвал if(inputStream.available() > 0) while((aByte = read()) > -1) хранить байт. Теперь он работал правильно, и все мои данные были отображены.

Может кто-нибудь объяснить это поведение? Говорят, что InputStream.available() возвращает число байтов, которое вы можете прочитать, перед блокировкой следующего вызывающего абонента (потока?). Даже если я не использую .available(), я бы ожидал, что чтение первого события просто заблокирует чтение второго события, но не удалит слишком много данных потока. Почему бы это сделать, чтобы не отображались все мои данные?

4b9b3361

Ответ 1

Основной источник данных для некоторых реализаций InputStream может сигнализировать о том, что конец потока достигнут, и больше данных не будет отправлено. Пока этот сигнал не будет принят, операции чтения в таком потоке могут блокироваться.

Например, InputStream из сокета Socket блокирует, а не возвращает EOF, пока не будет получен пакет TCP с установленным флажком FIN. Когда EOF принимается от такого потока, вы можете быть уверены, что все данные, отправленные на этот сокет, были надежно получены, и вы больше не сможете читать данные. (Если чтение блокировки приводит к исключению, с другой стороны, некоторые данные могут быть потеряны.)

В других потоках, например, из необработанного файла или последовательного порта, может отсутствовать аналогичный формат или протокол, чтобы указать, что больше данных не будет доступно. Такие потоки могут немедленно возвращать EOF (-1), а не блокировать, когда в настоящее время нет данных. Однако при отсутствии такого формата или протокола вы не можете быть уверены, когда другая сторона отправит данные.


Что касается вашего второго вопроса, похоже, что у вас, возможно, было состояние гонки. Не видя код, о котором идет речь, я предполагаю, что проблема на самом деле лежит в вашем методе "отображения". Возможно, попытка отобразить по второму уведомлению каким-то образом сбила работу, выполненную во время первого уведомления.

Ответ 2

Он возвращает -1, если это конец потока. Если поток все еще открыт (т.е. Соединение сокета), но данные не достигли стороны чтения (сервер медленный, сети медленны,...) блоки read().

Вам не нужен доступный вызов(). Мне сложно понять ваш дизайн уведомлений, но вам не нужны вызовы, кроме самого read(). Доступный метод() доступен только для удобства.

Ответ 3

Хорошо, это немного путаница, поэтому сначала давайте разберемся с этим: блокировка InputStream.read() не имеет ничего общего с многопоточностью. Если у вас есть несколько потоков, читающих из одного и того же входного потока, и вы запускаете два события очень близко друг к другу - где каждый поток пытается использовать событие, то вы получите повреждение: первый поток для чтения получит несколько байтов (возможно, все байты), и когда второй поток будет запланирован, он будет читать остальные байты. Если вы планируете использовать один поток ввода-вывода в более чем одном потоке, всегда synchronized() {} при некоторых внешних ограничениях.

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

Поведение для "больше нет доступных данных, пожалуйста, подождите, и вы получите больше" для read() чтобы заблокировать и не возвращать, пока не будут доступны некоторые данные (или возникнет исключение).

Ответ 4

По умолчанию поведение предоставленного RXTX InputStream не соответствует требованиям.

Вы должны установить порог приема равным 1 и отключить тайм-аут приема:

serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();

Источник: Последовательное соединение RXTX - проблема с блокировкой чтения()

Ответ 5

Да! Не сдавайся в своем потоке, но Юбу. Мы говорим о Серийной связи здесь. Для серийного материала абсолютно ожидаемо, что -1 может/будет возвращаться при чтении, но все же ожидает данных позднее. Проблема в том, что большинство людей используют TCP/IP, которые должны всегда возвращать 0, если TCP/IP не отключен... тогда да -1 имеет смысл. Тем не менее, с Serial нет потока данных в течение длительных периодов времени, и нет "HTTP Keep Alive", или TCP/IP-биение, или (в большинстве случаев) нет аппаратного управления потоком. Но ссылка физическая и все еще соединена "медью" и до сих пор прекрасно жива.

Теперь, если они говорят правильно, то есть: Serial должен быть закрыт на -1, тогда почему мы должны смотреть на такие вещи, как OnCTS, pmCarroerDetect, onDSR, onRingIndicator и т.д. Heck, если 0 означает его там, а -1 означает его нет, а затем вверните все эти функции обнаружения!: -)

Проблема, с которой вы можете столкнуться, может быть в другом месте.

Теперь, по спецификациям:

Q: "Казалось, что будет отображаться только конечный конец данных второго события, а остальное отсутствовало".

A: Я собираюсь предположить, что вы были в цикле, повторно используя один и тот же байт [] buffer. Первое сообщение приходит, пока не отображается на экране/журнале/std (потому что вы находитесь в цикле), тогда вы читаете второе сообщение, заменяя данные 1-го сообщения в буфере. Опять же, потому что я собираюсь предположить, что вы не храните, сколько вы читаете, а затем убедитесь, что ваш буфер хранилища смещен на предыдущую сумму чтения.

Q: "В конце концов я изменил свой код, чтобы при получении события, которое я вызывал if (inputStream.available() > 0), в то время как ((aByte = read()) > -1) байт".

A: Браво... это хороший материал. Теперь ваш буфер данных находится внутри оператора IF, ваше второе сообщение не будет сжимать ваш 1-й... ну, на самом деле, это было, пожалуй, только одно большое (э-э) сообщение в 1-м месте. Но теперь вы прочитаете все это одним выстрелом, сохранив данные без изменений.

C: "... состояние гонки..."

A: Ахх, хороший ол, поймать всех козлов! Состояние гонки...:-) Да, это может быть состояние гонки, на самом деле это могло быть хорошо. Но это может быть просто способ, которым RXTX очищает флаг. Очистка "доступного флага данных" может произойти не так быстро, как вы ожидаете. Например, кто-нибудь знает разницу между чтением VS readLine в отношении очистки буфера, который ранее был сохранен в данных, и повторной установкой флага события? Я тоже.:-) И я еще не могу найти ответ... но... позвольте мне рассказать о нескольких предложениях больше. Программирование, управляемое событиями, все еще имеет некоторые недостатки. Позвольте мне дать вам пример реального мира, с которым мне пришлось иметь дело в последнее время.

  • Я получил данные TCP/IP, скажем, 20 байт.
  • Поэтому я получаю OnEvent для полученных данных.
  • Я начинаю "читать" даже по 20 байтам.
  • Прежде чем закончить чтение 20 байтов, я получаю еще 10 байт.
  • Однако TCP/IP пытается уведомить меня, о, видит, что флаг по-прежнему установлен, и не будет уведомлять меня снова.
  • Тем не менее, я заканчиваю читать мои 20 байт (доступно() сказал, что их было 20)...
  • ... и последние 10 байтов остаются в TCP/IP Q... потому что я не был уведомлен о них.

См., уведомление было упущено, потому что флаг все еще установлен... хотя я начал читать байты. Если бы я закончил байты, тогда флаг был бы очищен, и я получил бы уведомление для следующих 10 байтов.

Точная противоположность того, что сейчас происходит для вас.

Итак, идите с доступным IF()... прочитайте возвращаемую длину данных. Затем, если вы параноик, установите таймер и вызовите доступный() еще раз, если там все еще есть данные, затем прочитайте новые данные. Если доступно() возвращает 0 (или -1), то расслабьтесь... откиньтесь назад... и дождитесь следующего уведомления OnEvent.

Ответ 6

InputStream - это всего лишь абстрактный класс, к сожалению, реализация решает, что происходит.

Что произойдет, если ничего не найдено:

  • Сокеты (т.е. SocketInputStream) будут блокироваться до тех пор, пока данные не будут получены (по умолчанию). Но можно установить тайм-аут (см. setSoTimeout), затем read будет блокировать x ms. Если все равно ничего не получено, будет выбрано SocketTimeoutException.

    Но с тайм-аутом или без него чтение из SocketInputStream может иногда приводить к -1. (например, когда несколько клиентов одновременно подключаются к одному и тому же host:port, тогда даже если устройства кажется связанным, результат read может немедленно восстановить в -1 (никогда не возвращать данные).)

  • Serialio связь всегда будет возвращаться -1; Вы также можете установить тайм-аут (используйте setTimeoutRx), read будет сначала блокировать x ms, но результат будет -1, если ничего не найдено. (Примечание: но есть несколько серийных классов io, поведение может быть зависимым от поставщика.)

  • Файлы (считыватели или потоки) приведут к EOFException.

Работа с общим решением:

  • Если вы переносите любой из вышеперечисленных потоков в DataInputStream, вы можете использовать такие методы, как readByte, readChar и т.д. Все значения -1 преобразуются в EOFException. (PS: если вы выполняете много небольших чтений, тогда рекомендуется сначала обернуть его в BufferedInputStream)
  • Оба SocketTimeoutException и EOFException расширяют IOException, и есть еще несколько возможных IOException. Удобно просто проверить IOException на обнаружение проблем связи.

Другая чувствительная тема - промывка. flush в терминах сокетов означает "отправить его сейчас", но в терминах Serialio это означает "отбросить буфер".

Ответ 7

Я использую eclipse Jetty 9.2.2. Я с удивлением вижу, что 10-12% моей заявки занимают около 500+ миллисекунд каждый, а другие обрабатываются в течение 2-3 миллисекунд каждый. часть кода, принимающего

timeBufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));

String line = null;
String postData = "";
while ((line = br.readLine()) != null){ // this is the line taking all time
    postData+=line;
}

br.close();

Ответ 8

Я думаю, вы можете получить весь поток данных, если используете thread.sleep()