Как создать Java неблокирующий InputStream из HttpsURLConnection?

По сути, у меня есть URL-адрес, который передает XML-обновления из чата при публикации новых сообщений. Я хотел бы превратить этот URL-адрес в InputStream и продолжать чтение из него, пока поддерживается соединение и пока я не отправил Thread.interrupt(). Проблема, с которой я сталкиваюсь, заключается в том, что BufferedReader.ready(), похоже, не становится истинным, когда есть контент, который нужно прочитать из потока.

Я использую следующий код:

BufferedReader buf = new BufferedReader(new InputStreamReader(ins));


String str = "";
while(Thread.interrupted() != true)
{
    connected = true;
    debug("Listening...");

    if(buf.ready())
    {
        debug("Something to be read.");
        if ((str = buf.readLine()) != null) {
            // str is one line of text; readLine() strips the newline character(s)
            urlContents += String.format("%s%n", str);
            urlContents = filter(urlContents);
        }
    }

    // Give the system a chance to buffer or interrupt.
    try{Thread.sleep(1000);} catch(Exception ee) {debug("Caught thread exception.");}
}

Когда я запускаю код и публикую что-то в чате, buf.ready() никогда не становится истинным, в результате чего строки никогда не читаются. Однако, если я пропущу часть «buf.ready()» и просто прочитаю строки напрямую, дальнейшие действия будут заблокированы до тех пор, пока строки не будут прочитаны.

Как мне либо а) заставить buf.ready() возвращать true, либо б) сделать это таким образом, чтобы предотвратить блокировку?

Заранее спасибо, Джеймс


person Warkior    schedule 19.02.2011    source источник
comment
Каждое соединение должно быть выделено в отдельный поток.   -  person Nick    schedule 19.02.2011


Ответы (5)


Как создать неблокирующий Java InputStream

Вы не можете. Ваш вопрос заключает в себе противоречие в терминах. Потоки в Java блокируются. Поэтому не существует такого понятия, как «неблокирующий InputStream».

Reader.ready() возвращает true, когда данные могут быть прочитаны без блокировки. Период. InputStreams и Readers блокируют. Период. Здесь все работает как задумано. Если вы хотите большего параллелизма с этими API, вам придется использовать несколько потоков. Или Socket.setSoTimeout() и его ближайший родственник в HttpURLConnection.

person user207421    schedule 19.02.2011
comment
Я знаю, что могу разделить вещи на потоки... этот код уже находится в своем собственном потоке (исполняемом) объекте. Я хочу знать, как остановить поток, отправив какое-то прерывание. Когда входной поток ожидает публикации дополнительных данных в потоке, кажется, что он блокирует все остальное, включая thread.interrupts. - person Warkior; 19.02.2011
comment
Можете ли вы описать ситуацию, когда Reader.ready() сможет вернуть true, если (как вы сказали) читатели естественным образом блокируются? Исходя из того, что вы сказали выше, может показаться, что Reader.ready() - бесполезный метод. - person Warkior; 19.02.2011
comment
Если данные уже доступны, ready() возвращает true и Reader не будет блокироваться. Если данные недоступны, ready() возвращает false, и Reader блокируется. - person user207421; 20.02.2011
comment
Итак, если я ЗНАЮ, что в потоке есть данные для чтения, почему buf.ready() продолжает возвращать false? Это та часть, которая меня смущает. Я ЗНАЮ, что в потоке есть данные, готовые к чтению. - person Warkior; 20.02.2011
comment
Вы не можете «знать» это. Только ready() знает об этом (и InputStream.available() в обоих случаях, где поддерживается). Другого теста нет. Для некоторых потоков, таких как SSL, ни один из них не поддерживается, поэтому ready() возвращает false, а available() возвращает ноль. Кроме того, существует разница между доступными данными и полной строкой, доступной для readLine(), включая признак конца строки. readLine() будет блокироваться до тех пор, пока все это не прибудет - person user207421; 21.02.2011

Для неблокирующего ввода-вывода не используйте InputStream и Reader (или OutputStream/Writer), а используйте классы java.nio.*, в данном случае SocketChannel (и дополнительно CharsetDecoder).


Изменить: как ответ на ваш комментарий:

В частности, ищите, как создать канал сокета для URL-адреса https.

Сокеты (а также SocketChannels) работают на транспортном уровне (TCP), на один (или два) уровня ниже протоколов прикладного уровня, таких как HTTP. Таким образом, вы не можете создать канал сокета для URL-адреса https.

Вместо этого вам нужно будет открыть Socket-Channel для правильного сервера и правильного порта (443, если ничего другого не указано в URI), создать SSLEngine (в javax.net.ssl) в режиме клиента, а затем прочитать данные из канала , передавая его механизму SSL и наоборот, и отправляя/получая правильные строки протокола HTTP в/из вашего SSLEngine, всегда проверяя возвращаемые значения, чтобы узнать, сколько байтов было фактически обработано и что будет следующим шагом для брать.

Это довольно сложно (я сделал это один раз), и вы действительно не хотите этого делать, если вы не реализуете сервер с большим количеством клиентов, подключенных одновременно (где вы не можете иметь один поток для каждого соединения). Вместо этого оставайтесь со своим блокирующим InputStream, который читает из вашего URLConnection, и поместите его просто в запасной поток, который не мешает остальной части вашего приложения.

person Paŭlo Ebermann    schedule 19.02.2011
comment
В частности, ищите, как создать канал сокета для URL-адреса https. - person Warkior; 19.02.2011
comment
@Warkior: посмотри мое последнее редактирование - ты действительно не хочешь этого делать. - person Paŭlo Ebermann; 19.02.2011
comment
Привет Павел, Спасибо за совет. Это имеет большой смысл и отвечает на мои основные опасения по поводу этого конкретного метода. У меня действительно нет контроля над сервером... только клиент, читающий из потока. Есть ли правильный способ разорвать заблокированное соединение в этой ситуации? Это должно произойти, если пользователь перейдет в новую комнату чата. (означает, что системе нужно начать прослушивание другого потока, завершив работу старого прослушивателя) - person Warkior; 20.02.2011


Нет реализации HTTP/HTTPS с использованием каналов. Невозможно прочитать входной поток из httpurlconnaction неблокирующим способом. Вам нужно либо использовать стороннюю библиотеку, либо самостоятельно реализовать http через SocketChannel.

person bla    schedule 10.03.2017

person    schedule
comment
Это не неблокирующий ввод-вывод. Это бесполезный и избыточный пример блокировки ввода-вывода с тайм-аутом, который уже можно выполнить с помощью тайм-аута чтения и SocketTimeoutException. Код явно даже не тестировался. InputStream.read(byte[]) не может возвращать ноль, если буфер не имеет нулевой длины, чего здесь нет. Вы не проверяете конец потока и не передаете его исходному вызывающему абоненту. String не является контейнером для потенциально двоичных данных. Ответ неверный во всех отношениях. - person user207421; 11.05.2018
comment
Спасибо за указанные логические ошибки. Я исправил их. В общем, этот код предназначен для запуска модульных тестов в текстовых серверных сокетах. Если вы предложите более элегантное решение, я с радостью им воспользуюсь. - person Valdis; 30.08.2018
comment
Никто не может предоставить код для проблемы, которая воплощает противоречие в терминах. - person user207421; 11.09.2018