Значение «java.io.IOException: Тайм-аут соединения» после фазы соединения

1

Может быть связано: разница между временем соединения и временем ожидания

Я написал приложение Java-сервера, используя nio.

Я подключил клиента к моему серверному приложению и отключил сетевой кабель от клиента. На стороне сервера я не получал никакого исключения сразу, но через некоторое время (8 минут или около того) у меня получилось "IOException: Connection timed out"

Вот частичная трассировка стека:

java.io.IOException: Connection timed out
    at sun.nio.ch.FileDispatcherImpl.read0(Native Method)
    at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:225)
    at sun.nio.ch.IOUtil.read(IOUtil.java:198)
    at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:375)
........

До этого момента, когда я увидел вывод netstat, я вижу, что состояние сокета этого конкретного клиентского соединения показано как ESTABLISHED.

Вопросы:

  1. Является ли этот тайм-аут настраиваемым?

  2. Почему вывод netstat показывает состояние сокета как ESTABLISHED? В идеале это должен быть CLOSE_WAIT (поскольку клиент был отключен)

Теги:
sockets
nio

4 ответа

2
Лучший ответ
  1. Нет, он не настраивается. Это результат тайм-аута повторной передачи. Это не произошло бы вообще, если приложение не продолжало писать, или ожидало записи, когда произошло отключение.

  2. Это не должно быть CLOSE_WAIT, поскольку FIN не был получен. Ergo должен быть УСТАНОВЛЕН.

  • 1
    Тайм-аут настраивается не в приложении, а на уровне операционной системы. Посмотрите на справочной странице Linux по tcp параметры tcp_retries1 и tcp_retries2.
2

Этот тайм-аут обычно не настраивается, поскольку он зависит от возможностей, предлагаемых операционной системой. Unix вообще не позволяет процессу фиксировать тайм-аут соединения и обычно фиксируется примерно до двух минут. Возможно, некоторые версии систем linux/BSD позволяют настроить это, но это не переносится и, как правило, не разрешено устанавливать его пользователю (только администратору). Это связано с количеством повторных передач и тайм-аутами, используемыми для каждой попытки, и находится под исключительным контролем реализации TCP.

Когда вы завершаете соединение, вы проходите через два состояния (FIN_WAIT и TIME_WAIT), которые не являются состояниями таймаута. Первый из двух - получить другой ответ конца (вы можете закрыть свою сторону соединения, указав другой стороне, что вы не собираетесь отправлять больше данных, но вам нужно подождать, пока другой конец сделает то же самое) TIME_WAIT является специальным состоянием, которое ядро поддерживает для закрытого подключения к процессу (и отбрасывает) все возможные повторные передачи последних кадров, которые могут быть в курсе после закрытия соединения. Они не имеют никакого отношения к таймаутам.

Соединение tcp не имеет таймаута. Две машины могут проходить недели без обмена информацией, если им нечего передавать. Вы можете контролировать использование какого-то биения между тихими соединениями, чтобы проверить их жизнеспособность с помощью опции сокета (SO_KEEPALIVE). Эта опция позволяет tcps с обеих сторон обменивать пустые пакеты, чтобы узнать, жива ли другая сторона. Опять же, вы можете контролировать использование этих пакетов, а не частоту или количество потерянных кадров, которые закрывают соединение (это можно настроить в Linux, но касаться конфигурации ядра только в режиме администратора)

Примечание 1 (ответ на вопрос @Krishna Chaitanya P)

Если вы отсоединили кабель и получили какое-то время спустя, это может быть одной из двух причин:

  1. Вы продолжаете писать на это соединение, и буфер отправки заполняется без подтверждения вовремя (это редко, так как обычно ваш процесс блокируется при записи (2) системного вызова, когда это происходит) и некоторый тайм-аут (в java-реализации сокета) произошло.
  2. В вашей реализации java tcp-сокета используется опция SO_KEEPALIVE (наиболее вероятная вещь). Как я уже говорил, у вас есть логический элемент управления, который использует или не использует его, но вы не можете настроить время между keepalives или количеством из них, которое снижает ваше соединение. Попробуйте вызвать методы getKeepAlive()/setKeepAlive (boolean) для класса Socket для управления этой функцией. Я не видел в документации, если подключенный сокет по умолчанию сохранен или нет. Это, безусловно, широко используемый вариант на сервере, поскольку он позволяет отключать клиентов, которые теряют соединения, не сообщая серверу.
0

По моему опыту причина возникновения этого исключения для подключенного сокета всегда была вызвана закрытием соединений брандмауэра, которые слишком долго простаивали. Я видел, как это происходит в облачных средах (AWS, Rackspace), в частности, но это не ограничивается этим. Скорее всего, у вас есть какой-то межсетевой экран между двумя одноранговыми соединениями, которые через некоторое время закрывают незанятые соединения.

Лучшим решением в идеальном мире является изменение конфигурации брандмауэра, если вы или операционная группа имеете к нему доступ. В любом случае, лучше, если вы можете обработать этот прецедент в своем коде и изящно прекратить связь с другим партнером.

  • 0
    Как я уже упоминал, я отключил кабель вручную. У меня нет межсетевого экрана между клиентом и сервером, они напрямую связаны между собой.
  • 0
    В этом случае брандмауэр отправляет пустые сегменты tcp с правильным порядковым номером (в обоих направлениях) и флагом RST, поэтому соединение активно сбрасывается его вмешательством. Это может произойти в случае записи NAT в тайм-аутах таблиц, но не часто можно увидеть, как она реализована.
-3

Поскольку состояние CLOSE_WAIT для FI ожидает своего соответствующего FIN от однорангового узла, и это не так.

Этот ТО, вероятно, настраивается

  • 0
    Нет. Порт в состоянии CLOSE_WAIT уже получил FIN от однорангового узла и ожидает закрытия локального приложения.
  • 0
    Это ТО определенно не настраивается. Если ожидающий завершения процесс не делает этого, зачем прерывать его событием тайм-аута? В вызове close (2) нет никакой возможной потери информации, чтобы сделать необходимым информирование процесса о том, что он не закрыл сокет, чтобы сделать этот таймаут необходимым.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню