Имеет ли TCP-сокет соединение "keep alive"?


Я слышал о HTTP keep-alive, но сейчас я хочу открыть соединение сокета с удаленным сервером.
Теперь это соединение сокета останется открытым навсегда или с ним связан лимит времени ожидания, подобный HTTP keep-alive?

6 68

6 ответов:

TCP сокеты остаются открытыми, пока они не будут закрыты.

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

теперь это соединение сокета останется открытым навсегда или с ним связан лимит времени ожидания, подобный HTTP keep-alive?

короткий ответ:да, есть тайм-аут, и он применяется через TCP Keep-Alive.

если вы хотите настроить время ожидания Keep-Alive, см. раздел "изменение таймаутов TCP" ниже.

введение

TCP-соединения состоят из двух розеток, одна на каждом конце соединения. Когда одна сторона хочет прервать соединение, она отправляет RST пакет, который другая сторона признает, и оба закрывают свои сокеты.

до тех пор, пока это не произойдет, однако, обе стороны будут держать их сокет открытым на неопределенный срок. Это оставляет открытой возможность того, что одна сторона может закрыть свой сокет, намеренно или из-за некоторой ошибки, не сообщая другой конец через RST. Для обнаружения этого сценария и закрытия устаревших соединений Используется процесс TCP Keep Alive.

Keep-Alive Process

есть три настраиваемых свойства, которые определяют, как Keep-Alives работают. На Linux они1:

  • tcp_keepalive_time
    • по умолчанию 7200 секунд
  • tcp_keepalive_probes
    • по умолчанию 9
  • tcp_keepalive_intvl
    • по умолчанию 75 секунды

процесс работает следующим образом:

  1. клиент открывает TCP-соединение
  2. если соединение молчит для tcp_keepalive_time секунд, отправить один пустой ACK пакета.1
  3. сервер ответил соответствующим ACK своего собственного?
    • нет
      1. ждать tcp_keepalive_intvl секунд, а затем отправьте другой ACK
      2. повторять до тех пор, пока число ACK зонды, которые были отправлены, равны tcp_keepalive_probes.
      3. если на данный момент не было получено ответа, отправьте RST и прекратить связь.
    • да: вернуться к Шагу 2

этот процесс включен по умолчанию в большинстве операционных систем, и поэтому мертвые TCP-соединения регулярно обрезаются, как только другой конец не отвечает в течение 2 часов 11 минут (7200 секунд + 75 * 9 секунд).

Gotchas

2 Часа По Умолчанию

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

Keep-Alive является необязательным

по данным RFC 1122 4.2.3.6, реагирование и / или ретрансляция TCP Keep-Alive packets необязательно:

разработчики могут включать "keep-alives" в свои реализации TCP, хотя эта практика не является общепринятой. Если держать-живы будут в комплекте, приложение должно быть в состоянии включить или выключить их для каждого TCP-соединение, и они должны по умолчанию отключаться.

...

это чрезвычайно важно помнить, что сегменты ACK, которые не содержат данных, не являются надежно передается по протоколу TCP.

рассуждение заключается в том, что пакеты Keep-Alive не содержат данных и не являются строго необходимыми и рискуют засорить трубки паутины, если их чрезмерно использовать.

однако на практике, мой опыт показывает, что эта проблема со временем уменьшилась, поскольку пропускная способность стала дешевле; и поэтому пакеты Keep-Alive обычно не отбрасываются. документация Amazon EC2 например, дает косвенное одобрение Keep-Alive, поэтому, если вы размещаете с AWS, вы, вероятно, безопасно полагаетесь на Keep-Alive, но ваш пробег может отличаться.

изменение таймаутов TCP

За Исполнение

к сожалению, поскольку TCP-соединения управляются на уровне ОС, Java не поддерживает настройку таймаутов на уровне сокетов, таких как in java.net.Socket. Я нашел несколько попыток3 чтобы использовать Java Native Interface (JNI) для создания сокетов Java, которые вызывают собственный код для настройки эти варианты, но ни один из них, по-видимому, не имеет широкого принятия или поддержки сообщества.

вместо этого, вы можете быть вынуждены применить конфигурацию операционной системы в целом. Имейте в виду, что эта конфигурация будет влиять на все TCP-соединения, работающие на всей системе.

Linux

в настоящее время настроен протокол TCP удержание параметров может быть найден в

  • /proc/sys/net/ipv4/tcp_keepalive_time
  • /proc/sys/net/ipv4/tcp_keepalive_probes
  • /proc/sys/net/ipv4/tcp_keepalive_intvl

вы можете обновить любой из них следующим образом:

# Send first Keep-Alive packet when a TCP socket has been idle for 3 minutes
$ echo 180 > /proc/sys/net/ipv4/tcp_keepalive_time
# Send three Keep-Alive probes...
$ echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
# ... spaced 10 seconds apart.
$ echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl

такие изменения не будут сохраняться при перезапуске. Для внесения постоянных изменений используйте sysctl:

sysctl -w net.ipv4.tcp_keepalive_time=180 net.ipv4.tcp_keepalive_probes=3 net.ipv4.tcp_keepalive_intvl=10

Mac OS X

текущие настройки можно просмотреть с помощью sysctl:

$ sysctl net.inet.tcp | grep -E "keepidle|keepintvl|keepcnt"
net.inet.tcp.keepidle: 7200000
net.inet.tcp.keepintvl: 75000
net.inet.tcp.keepcnt: 8

обратите внимание, Mac OS X определяет keepidle и keepintvl в единицах МС в отличие от Linux, который использует секунды.

свойства могут быть установлены с помощью sysctl который сохранит эти настройки при перезагрузке:

sysctl -w net.inet.tcp.keepidle=180000 net.inet.tcp.keepcnt=3 net.inet.tcp.keepintvl=10000

кроме того, вы можете добавлять их в /etc/sysctl.conf (создает файл, если он не существует).

$ cat /etc/sysctl.conf
net.inet.tcp.keepidle=180000
net.inet.tcp.keepintvl=10000
net.inet.tcp.keepcnt=3

Windows

у меня нет машины Windows для подтверждения, но вы должны найти соответствующие настройки TCP Keep-Alive в реестре в

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TCPIP\Parameters

сноски

1. Смотрите man tcp для получения дополнительной информации.

2. Этот пакет часто называют" Keep-Alive " пакет, но в спецификации TCP это просто обычный ACK пакета. Такие приложения, как Wireshark, могут помечать его как" живой " пакет путем мета-анализа последовательности и номеров подтверждения, которые он содержит в ссылка на предыдущие сообщения в сокете.

3. Некоторые примеры, которые я нашел в основном поиске Google являются lucwilliams / JavaLinuxNet и flonatel / libdontdie.

вы ищете параметр сокета SO_KEEPALIVE.

The Java Socket API выставляет" keep-alive " для приложений через setKeepAlive и getKeepAlive методы.

EDIT: SO_KEEPALIVE реализован в стеках сетевых протоколов ОС без отправки каких-либо" реальных " данных. Интервал поддержания активности зависит от операционной системы и может быть настроен с помощью параметра ядра.

поскольку данные не отправляются, SO_KEEPALIVE может только проверить живучесть сетевое подключение, а не живость службы, к которой подключен сокет. Чтобы проверить последнее, вам нужно реализовать что-то, что включает в себя отправку сообщений на сервер и получение ответа.

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

Это из спецификации TCP,

пакеты Keep-alive должны отправляться только в том случае, если в течение интервала не было получено никаких данных или пакетов подтверждения для соединения. Этот интервал должен быть конфигурируемым и по умолчанию должен быть не менее двух несколько часов.

Как вы можете видеть, по умолчанию TCP keepalive интервал слишком длинный для большинства приложений. Возможно, вам придется добавить активности в свой протокол.

Если вы находитесь за маскирующимся NAT (как и большинство домашних пользователей в эти дни), существует ограниченный пул внешних портов, и они должны быть общими для TCP-соединений. Поэтому маскирующиеся NATs, как правило, предполагают, что соединение было прервано, если данные не были отправлены в течение определенного периода времени.

эта и другие подобные проблемы (где-нибудь между двумя конечными точками) могут означать, что соединение больше не будет "работать", если вы попытаетесь отправить данные после разумного периода простоя. Тем не менее, вы не можете обнаружить это, пока вы попробовать для отправки данных.

используя keepalives оба уменьшается вероятность прерывания соединения где-то вниз по линии, а также позволяет узнать о сломанном соединении раньше.

вот некоторая дополнительная литература по keepalive, которая объясняет это гораздо более подробно.

http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO

поскольку Java не позволяет вам контролировать фактическое время поддержания активности, вы можете использовать примеры для их изменения, если вы используете ядро Linux (или ОС на основе proc).