Как убить вызов BufferedInputStream .read()

Я работаю над написанием программы для загрузки очень больших файлов (~ 2 ГБ) с сервера. Я написал программу, чтобы иметь возможность возобновить частично завершенные загрузки,

Чтобы имитировать плохое подключение к Интернету, я выдергивал сетевой шнур из маршрутизатора во время загрузки. К сожалению, это приводит к зависанию моей программы на следующем вызове: while((bytesRead = in.read(data)) > 0)

(Где bytesRead — это int, in — это BufferedInputStream, созданный из HttpURLConnection, а data — это массив байтов).

Я попытался «прервать» вызов, вызвав in.close() в другом потоке, но это не имеет никакого эффекта, пока интернет-соединение не будет восстановлено (в это время выдается исключение).

Есть ли способ предотвратить зависание моей программы из-за разорванного интернет-соединения?


person Samusaaron3    schedule 27.01.2012    source источник
comment
Есть похожие ответы на вопросы: stackoverflow.com/questions/804951/   -  person Joop Eggen    schedule 28.01.2012


Ответы (3)


Единственный надежный способ, который я нашел, - это создать экземпляр Socket как InterruptibleChannel и выполнить прерывание в зависшем потоке ввода-вывода. (Кстати, вам не нужно использовать асинхронные вызовы NIO с InterruptibleChannels, блокировка ввода-вывода работает нормально, у вас просто есть действительно хороший и унифицированный способ выгнать застрявшие обмены)

Хотя похоже, что URLConnection не позволяет вам подключить собственную фабрику сокетов.

Возможно, вам следует исследовать HttpClient от Apache.

ИЗМЕНИТЬ

Вот как вы создаете Interruptible Socket.

import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;

final SocketAddress remoteAddr =
    new InetSocketAddress(
        serverAddress,
        servicePort
    );

final SocketChannel socketChannel = SocketChannel.open( );

socketChannel.connect( remoteAddr );

// Here java.io.Socket is obtained
Socket socket = socketChannel.socket( );

У меня нет образца HttpClient, но я знаю, что вы можете настроить инициализацию сокета.

person Alexander Pogrebnyak    schedule 28.01.2012
comment
У вас есть примеры этого? Я искал довольно долгое время, и у меня были проблемы с поиском чего-либо полезного. По сути, мне нужен надежный, прерываемый, возобновляемый способ загрузки очень больших файлов (~ 2 ГБ). Я не могу найти эквивалент HttpURLConnection.setRequestProperty()... - person Samusaaron3; 28.01.2012
comment
@Самусаарон3. Я добавил пример того, как вы создаете взаимозаменяемый сокет. - person Alexander Pogrebnyak; 28.01.2012

См. http://thushw.blogspot.com/2010/10/java-urlconnection-provides-no-fail.html для кода, обрабатывающего эту ситуацию.

Отредактировано: на самом деле лучше установить время ожидания сокета (в миллисекундах) с помощью setSoTimeout (как это предлагается в комментарии к ссылке от Joop Eggen).

person DNA    schedule 27.01.2012
comment
Я пытался вызвать .disconnect() из отдельного потока, но даже после вызова IOException не выдается, поэтому я все еще застрял... - person Samusaaron3; 28.01.2012
comment
@DNA Чтобы использовать setSoTimeout, у вас должен быть сокет, но HttpURLConnection не позволяет вам получить его базовый сокет. (Я предполагаю, что OP должен полагаться на HttpURLConnection) - person Unai Vivi; 28.01.2012

У вас есть .setReadTimeout(int timeout) на вашем URLConnection?

-- РЕДАКТИРОВАТЬ

Смотрите ответ от @DNA для аккуратного решения:

Короче говоря, вы можете создать параллельный поток, который .disconnect() с URLConnection (после того, как ваш второй поток будет спать в течение тайм-аута миллисекунд), тем самым запустив IOException, который выведет вас из остановленного чтения.

person Unai Vivi    schedule 27.01.2012
comment
Это хорошая практика, но она не поможет, если сервер вернет какие-то данные и затем застрянет — это работает только в том случае, если сервер вообще не возвращает никаких данных. - person DNA; 28.01.2012