Использование inputStream.available ()
System.in.available () всегда допустимо возвращать 0.
Я обнаружил обратное - он всегда возвращает лучшее значение для количества доступных байтов. Javadoc для InputStream.available()
:
Returns an estimate of the number of bytes that can be read (or skipped over)
from this input stream without blocking by the next invocation of a method for
this input stream.
Оценка неизбежна из-за сроков / устаревания. Эта цифра может быть разовой заниженной, поскольку постоянно поступают новые данные. Однако он всегда «догоняет» при следующем вызове - он должен учитывать все поступившие данные, за исключением того, что он поступил только в момент нового вызова. Постоянно возвращать 0 при наличии данных не соответствует условию выше.
Первое предупреждение: за доступность отвечают конкретные подклассы InputStream
InputStream
это абстрактный класс. У него нет источника данных. Бессмысленно иметь доступные данные. Следовательно, Javadoc для available()
также утверждает:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
И действительно, конкретные классы входного потока переопределяют available (), предоставляя значимые значения, а не постоянные 0.
Второе предупреждение: убедитесь, что вы используете возврат каретки при вводе ввода в Windows.
При использовании System.in
ваша программа получает ввод только тогда, когда командная оболочка передает его. Если вы используете перенаправление файлов / pipe (например, somefile> java myJavaApp или somecommand | java myJavaApp), то входные данные обычно передаются немедленно. Однако, если вы вводите вручную, передача данных может быть отложена. Например, в оболочке windows cmd.exe данные буферизуются в оболочке cmd.exe. Данные передаются только в исполняющую Java-программу после возврата каретки (control-m или <enter>
). Это ограничение среды исполнения. Конечно, InputStream.available () будет возвращать 0 до тех пор, пока оболочка буферизует данные - это правильное поведение; на данный момент нет доступных данных. Как только данные доступны из оболочки, метод возвращает значение> 0. Примечание: Cygwin использует cmd.
Самое простое решение (без блокировки, поэтому не требуется тайм-аут)
Просто используйте это:
byte[] inputData = new byte[1024];
int result = is.read(inputData, 0, is.available());
// result will indicate number of bytes read; -1 for EOF with no data read.
ИЛИ эквивалентно,
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, Charset.forName("ISO-8859-1")),1024);
// ...
// inside some iteration / processing logic:
if (br.ready()) {
int readCount = br.read(inputData, bufferOffset, inputData.length-bufferOffset);
}
Richer Solution (максимально заполняет буфер в течение периода ожидания)
Объявите это:
public static int readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis)
throws IOException {
int bufferOffset = 0;
long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length) {
int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
// can alternatively use bufferedReader, guarded by isReady():
int readResult = is.read(b, bufferOffset, readLength);
if (readResult == -1) break;
bufferOffset += readResult;
}
return bufferOffset;
}
Тогда используйте это:
byte[] inputData = new byte[1024];
int readCount = readInputStreamWithTimeout(System.in, inputData, 6000); // 6 second timeout
// readCount will indicate number of bytes read; -1 for EOF with no data read.
is.available() > 1024
это предложение не удастся. Конечно, есть потоки, которые возвращают ноль. SSLSockets например до недавнего времени. Вы не можете полагаться на это.Предполагая, что ваш поток не поддерживается сокетом (поэтому вы не можете его использовать
Socket.setSoTimeout()
), я думаю, что стандартный способ решения этого типа проблем - это использование Future.Предположим, у меня есть следующий исполнитель и потоки:
У меня есть писатель, который пишет некоторые данные, а затем ждет в течение 5 секунд, прежде чем записать последний кусок данных и закрытия потока:
Нормальный способ чтения этого заключается в следующем. Чтение будет блокироваться на неопределенный срок для данных, и это завершается через 5 секунд:
какие выводы:
Если бы существовала более фундаментальная проблема, например, что писатель не отвечает, читатель заблокирует его навсегда. Если я заверну чтение в будущем, то смогу контролировать время ожидания следующим образом:
какие выводы:
Я могу поймать TimeoutException и сделать любую очистку, какую захочу.
источник
executer.shutdownNow
не убьет нить. Он будет пытаться прервать его, но безрезультатно. Очистка невозможна, и это серьезная проблема.Если ваш InputStream поддерживается Socket, вы можете установить время ожидания Socket (в миллисекундах), используя setSoTimeout . Если вызов read () не разблокируется в течение указанного времени, он выдаст исключение SocketTimeoutException.
Просто убедитесь, что вы вызываете setSoTimeout для Socket перед выполнением вызова read ().
источник
Я бы поставил вопрос о постановке проблемы, а не просто принял ее вслепую. Вам нужны только тайм-ауты из консоли или по сети. Если последний у вас есть,
Socket.setSoTimeout()
иHttpURLConnection.setReadTimeout()
оба делают именно то, что требуется, при условии, что вы правильно настроили их, когда строите / приобретаете их. Оставляя его на произвольном этапе позже в приложении, когда все, что у вас есть, это InputStream - плохой дизайн, приводящий к очень неловкой реализации.источник
Я не использовал классы из пакета Java NIO, но, похоже, они могут быть здесь полезны. В частности, java.nio.channels.Channels и java.nio.channels.InterruptibleChannel .
источник
Вот способ получить NIO FileChannel из System.in и проверить доступность данных, используя тайм-аут, который является частным случаем проблемы, описанной в вопросе. Запустите его на консоли, не вводите никаких данных и ждите результатов. Он был успешно протестирован под Java 6 на Windows и Linux.
Интересно, что при запуске программы внутри NetBeans 6.5, а не на консоли, тайм-аут не работает вообще, и вызов System.exit () фактически необходим для уничтожения потоков зомби. Что происходит, так это то, что поток прерывания блокирует (!) При вызове reader.interrupt (). Другая тестовая программа (здесь не показана) дополнительно пытается закрыть канал, но это тоже не работает.
источник
Как сказал jt, NIO - лучшее (и правильное) решение. Если вы действительно застряли с InputStream, вы можете либо
Инициируйте поток, чья эксклюзивная работа - читать из InputStream и помещать результат в буфер, который можно прочитать из вашего исходного потока без блокировки. Это должно работать хорошо, если у вас есть только один экземпляр потока. В противном случае вы можете убить поток, используя устаревшие методы в классе Thread, хотя это может привести к утечке ресурсов.
Положитесь на isAvailable, чтобы указать данные, которые могут быть прочитаны без блокировки. Однако в некоторых случаях (например, в случае с Sockets) может потребоваться потенциально блокирующее чтение для isAvailable, чтобы сообщить о чем-то отличном от 0.
источник
Socket.setSoTimeout()
одинаково правильное и гораздо более простое решение. ИлиHttpURLConnection.setReadTimeout()
.Вдохновленный этим ответом, я придумал немного более объектно-ориентированное решение.
Это верно только в том случае, если вы собираетесь читать символы
Вы можете переопределить BufferedReader и реализовать что-то вроде этого:
Вот почти полный пример.
Я возвращаю 0 для некоторых методов, вы должны изменить его на -2 для удовлетворения ваших потребностей, но я думаю, что 0 больше подходит для контракта BufferedReader. Ничего плохого не произошло, просто прочитал 0 символов. Метод readLine - ужасный убийца производительности. Вы должны создать совершенно новый BufferedReader, если вы действительно хотите использовать readLin e. Прямо сейчас это не потокобезопасно. Если кто-то вызывает операцию, пока readLines ожидает строку, это приведет к неожиданным результатам.
Я не люблю возвращать -2 там, где я есть. Я бы бросил исключение, потому что некоторые люди могут просто проверять, если int <0, рассмотреть EOS. В любом случае, эти методы утверждают, что «не могут блокировать», вы должны проверить, является ли это утверждение истинным и просто не переопределять их.
источник