Как изящно очистить соединение HTTPURLC при обнаружении исключения? - PullRequest
6 голосов
/ 01 февраля 2012

У меня есть 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 или в системном журнале на сервере.

Ответы [ 2 ]

1 голос
/ 01 февраля 2012

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

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

1 голос
/ 01 февраля 2012

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

HTTPURLConnection 

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

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

...