Может быть связано: разница между временем соединения и временем ожидания
Я написал приложение 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.
Вопросы:
Является ли этот тайм-аут настраиваемым?
Почему вывод netstat показывает состояние сокета как ESTABLISHED? В идеале это должен быть CLOSE_WAIT (поскольку клиент был отключен)
Нет, он не настраивается. Это результат тайм-аута повторной передачи. Это не произошло бы вообще, если приложение не продолжало писать, или ожидало записи, когда произошло отключение.
Это не должно быть CLOSE_WAIT, поскольку FIN не был получен. Ergo должен быть УСТАНОВЛЕН.
Этот тайм-аут обычно не настраивается, поскольку он зависит от возможностей, предлагаемых операционной системой. Unix вообще не позволяет процессу фиксировать тайм-аут соединения и обычно фиксируется примерно до двух минут. Возможно, некоторые версии систем linux/BSD позволяют настроить это, но это не переносится и, как правило, не разрешено устанавливать его пользователю (только администратору). Это связано с количеством повторных передач и тайм-аутами, используемыми для каждой попытки, и находится под исключительным контролем реализации TCP.
Когда вы завершаете соединение, вы проходите через два состояния (FIN_WAIT и TIME_WAIT), которые не являются состояниями таймаута. Первый из двух - получить другой ответ конца (вы можете закрыть свою сторону соединения, указав другой стороне, что вы не собираетесь отправлять больше данных, но вам нужно подождать, пока другой конец сделает то же самое) TIME_WAIT является специальным состоянием, которое ядро поддерживает для закрытого подключения к процессу (и отбрасывает) все возможные повторные передачи последних кадров, которые могут быть в курсе после закрытия соединения. Они не имеют никакого отношения к таймаутам.
Соединение tcp не имеет таймаута. Две машины могут проходить недели без обмена информацией, если им нечего передавать. Вы можете контролировать использование какого-то биения между тихими соединениями, чтобы проверить их жизнеспособность с помощью опции сокета (SO_KEEPALIVE). Эта опция позволяет tcps с обеих сторон обменивать пустые пакеты, чтобы узнать, жива ли другая сторона. Опять же, вы можете контролировать использование этих пакетов, а не частоту или количество потерянных кадров, которые закрывают соединение (это можно настроить в Linux, но касаться конфигурации ядра только в режиме администратора)
Если вы отсоединили кабель и получили какое-то время спустя, это может быть одной из двух причин:
По моему опыту причина возникновения этого исключения для подключенного сокета всегда была вызвана закрытием соединений брандмауэра, которые слишком долго простаивали. Я видел, как это происходит в облачных средах (AWS, Rackspace), в частности, но это не ограничивается этим. Скорее всего, у вас есть какой-то межсетевой экран между двумя одноранговыми соединениями, которые через некоторое время закрывают незанятые соединения.
Лучшим решением в идеальном мире является изменение конфигурации брандмауэра, если вы или операционная группа имеете к нему доступ. В любом случае, лучше, если вы можете обработать этот прецедент в своем коде и изящно прекратить связь с другим партнером.
Поскольку состояние CLOSE_WAIT для FI ожидает своего соответствующего FIN от однорангового узла, и это не так.
Этот ТО, вероятно, настраивается