Как сокеты Java TCP / IP сообщают об успешной или неудачной передаче приложения? - PullRequest
5 голосов
/ 20 мая 2011

У меня проблема с сокетами Java TCP / IP: мое приложение Java будет продолжать бесконечно отправлять данные на сервер, даже если сервер будет отключен (без надлежащего отключения TCP / IP).

Я использую следующий код для отправки данных:

PrintWriter out = PrintWriter(socket.getOutputStream(), true);
out.write("text");

if (out.checkError()) {
   System.err.println("Error sending string!");
}

В другом вопросе переполнения стека я нашел следующий ответ:

TCP / IP (и, следовательно, Java-сокеты) будет гарантировать, что вы либо успешно отправить данные или получить ошибка (исключение в случае Java) в конце концов.

Достаточно ли моего кода для получения информации о том, что стек TCP / IP не может успешно отправить мою строку, или мне нужно что-то сделать дополнительно?

Кстати: было ли правильно открыть новый вопрос, хотя другой вопрос был похож? Он не ответил на мой вопрос удовлетворительно, и я смог добавить только новый ответ, а не новый комментарий.

Ответы [ 5 ]

4 голосов
/ 20 мая 2011

У вас могут быть две проблемы: PrintWriter - странный зверь в мире Java Stream / Reader / Writer, в котором он проглатывает исключения и требует от вас явной проверки на наличие ошибок.

Единственное использование для этого (на мой взгляд) - стандартные потоки (System.out и System.err), где сбой записи вывода не должен останавливать приложение (например, если нет стандартного вывода).

Замените его на OutputStreamWriter, и вы получите уведомление об ошибках, как только Java узнает о них.

Это подводит меня ко второй возможной проблеме: TCP / IP не имеет автоматических пакетов поддержки активности: поэтому если ваше соединение каким-либо образом разорвано, вы на самом деле не заметите его, пока Вы пытаетесь отправить данные.

Так что, если вы подключитесь к некоторому сокету, отправите несколько пакетов, подождите немного и , а затем отключитесь, вы будете уведомлены о том факте только при следующей попытке отправить некоторые данные. Это присуще протоколу TCP / IP и не является ошибкой Java.

Если вы хотите уменьшить проблему, вы можете отправлять периодические сообщения keep-alive / ping без какого-либо действительного эффекта, за исключением того, что они проверяют, все ли соединение еще живо.

1 голос
/ 13 июля 2011

Что я не упомянул (поскольку я думал, что это не было актуально в то время), так это то, что я пытаюсь сделать это на Android.Реализация стандартных сетевых классов Java ведет себя совсем не так, как Oracle JRE.На Android, по-видимому, невозможно надежно определить, было ли соединение закрыто, даже если я сам его закрыл.[Stream].write() постараюсь писать несколько минут.Так что в Android кажется, что вам всегда нужно будет отправлять свои собственные контрольные сообщения (и проверять на получение!) Для обнаружения разорванного соединения.

Другие ответы на этот вопрос будут хорошо работать с Oracle JRE,Еще раз спасибо!

Если кто-то может предоставить дополнительную информацию по этой теме, сделайте это.

1 голос
/ 20 мая 2011

Ваш вопрос достаточно отличается, я бы сказал. Google может многое сказать об этом. Keep-alives (SO_KEEPALIVE) не предназначены для такого рода вещей. Из того, что я прочитал, в спецификации TCP говорится, что они должны , а не отправляться чаще, чем раз в два часа, и ваша операционная система должна управлять этим, чтобы у вас не было большого контроля. Я подчеркиваю из того, что я прочитал .

Можно ли использовать их чаще, чем раз в два часа, в вашем случае не имеет значения, поскольку вы постоянно отправляете данные. Keep-alive нужны только в том случае, если вы хотите обнаружить разорванное соединение, пока вы не отправляете данные.

Если бы вы использовали OutputStream из Socket напрямую, это вызвало бы исключение при попытке отправки в недоступное место назначения (по какой бы то ни было причине). Поскольку вы используете PrintWriter, вам нужно вручную проверять наличие ошибок, используя checkError().

Итак, в заключение: да, этого достаточно для вашей цели.

0 голосов
/ 20 мая 2011

Одно специальное условие применяется, когда вы закрываете сокет.В зависимости от установленного сокета opt SO_LINGER (см. Эту статью ), вызов close() будет либо немедленно возвращаться, либо ждать, пока все ожидающие передачи TCP не будут либо успешными, либо произойдет ошибка, о которой затем будет сообщенос помощью IOException, генерируемого реализацией close().

Также SO_TIMEOUT влияет на время ожидания повторных попыток.

0 голосов
/ 20 мая 2011

Иногда, даже если сервер умер, ваше клиентское приложение не информируется об этом. Обычно это плохой маршрутизатор, который не может отключить обе стороны соединения. Вы должны настроить keep-alives , чтобы вы могли обнаружить этот тип ошибки.

В зависимости от вашей ОС существуют способы изменения интервала проверки активности.

...