Как изящно очистить HTTPURLConnection при обнаружении исключения?

У меня есть апплет Java, который использует класс HTTPURLConnection для загрузки очень больших файлов на веб-сервер IIS-7. Апплет разбивает файлы на куски, а затем отправляет их с помощью потоковой передачи фиксированной длины в PHP-скрипт.

Иногда при загрузке фрагментов файлов сетевое соединение между клиентом и сервером загадочным образом обрывается. Когда это происходит, мой вызов метода writeBytes() выдает IOException, который я перехватываю. Перехватив это исключение, я опускаюсь в свой блок finally, где пытаюсь все навести порядок. Поскольку в соединение было записано недостаточно данных (помните, что это потоковая передача фиксированной длины), попытка закрыть выходной поток также не удалась. В результате соединение, по-видимому, остается открытым (т. е. базовый сокет остается открытым). Кажется, это подтверждается просмотром исходный код метода close() для класса StreamingOutputStream (обратите внимание на комментарий о том, что сокет не может быть закрыт).

Вопрос:

Есть ли изящный способ, которым я могу закрыться после того, как поймал IOException во время записи в HTTPURLConnection? Говорить об объекте HTTPURLConnection disconnect() кажется недостаточным.

Дополнительная информация:

Вот первое исключение, которое я вижу, когда сеть перегружена, вызванная моим вызовом метода writeBytes() класса HTTPURLConnection:

java.io.IOException: Error writing request body to server
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.checkError(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(Unknown Source)
    at java.io.DataOutputStream.write(Unknown Source)
    at MultiPartPostThread.run(MultiPartPostThread.java:321)

Я перехватываю это исключение, а затем пытаюсь закрыть объект DataOutputStream, который я использовал для записи в соединение. Когда я это делаю, я получаю это исключение:

java.io.IOException: insufficient data written
    at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.close(Unknown Source)
    at java.io.FilterOutputStream.close(Unknown Source)
    at MultiPartPostThread.run(MultiPartPostThread.java:370)

Я могу убедиться, что сокет остается открытым, отметив, что для появления записи об ошибке в журнале IIS требуется 10 минут. Эта задержка (10 минут) является моим значением времени ожидания соединения. Примеры строк из журнала IIS (обрезаны для краткости):

2012-01-31 20:20:07 POST /upload_handler.php 200 0 0 356 26215490 3666
2012-01-31 20:20:10 POST /upload_handler.php 200 0 0 356 26215490 3853
2012-01-31 20:30:22 POST /upload_handler.php 500 0 64 0 15286099 611442

Обратите внимание, что в первых двух строках лога мы хорошо переносим: каждый раз отправляется 25 МБ, а возвращается код состояния 200. Сбой затем проявляется через 10 минут после последней успешной передачи с бесполезной ошибкой 500 (и обратите внимание, что это была только частичная передача; передано всего ~ 15 МБ). На самом деле это загадочное отключение происходит примерно через 1 минуту после начала всей процедуры, поэтому сообщения об истечении времени ожидания, которые я вижу в своих журналах трассировки IIS, являются отвлекающим маневром. Я не вижу ничего полезного в своих журналах PHP, журнале HTTPERR или системном журнале на сервере.


person Jonah Bishop    schedule 01.02.2012    source источник


Ответы (2)


Вызов close() в вашем потоке должен освободить ресурсы, используемые

HTTPURLConnection 

Вот примечание из HTTPURLConnection javadoc

Вызов методов close() для InputStream или OutputStream HttpURLConnection после запроса может освободить сетевые ресурсы, связанные с этим экземпляром, но не влияет на какое-либо общее постоянное соединение. Вызов метода разъединения() может закрыть базовый сокет, если постоянное соединение в это время бездействует.

person kosa    schedule 01.02.2012
comment
Проблема в том, что сам вызов close() выдает исключение (т. е. не удается). Это в дополнение к IOException, вызванному записью. - person Jonah Bishop; 01.02.2012
comment
Тогда единственный вариант — отключить(). Вы можете проверить это интересное обсуждение SO /а> - person kosa; 01.02.2012
comment
Я называю разъединение частью моей окончательной очистки. Кажется, это не помогает. Может быть, у меня нет соответствующего доступа к недрам этого класса, чтобы решить эту проблему... - person Jonah Bishop; 01.02.2012

Чем больше я изучаю это, тем больше убеждаюсь, что изящная очистка невозможна. Кажется, что вызова disconnect() недостаточно, чтобы закрыть базовый сокет; вам просто нужно подождать, пока время истечет.

Я думаю, что попробую использовать HttpClient Apache HttpComponents (как рекомендовано внизу этого потрясающего поста SO wiki), чтобы увидеть, сможет ли его встроенная обработка ошибок позаботиться об этом странном случае, который я вижу.

person Jonah Bishop    schedule 01.02.2012