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

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

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 секунды!--57-->

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

  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 пакеты необязательно:

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

...

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

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

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

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

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

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

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

Linux

текущие настройки TCP Keep-Alive можно найти в

  • /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 в реестре at

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

сноски

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

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

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


вы ищете опцию сокета SO_KEEPALIVE.

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

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

поскольку данные не отправляются, 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 не позволяет вам контролировать фактическое время keepalive, вы можете использовать примеры для их изменения, если вы используете ядро Linux (или ОС на основе proc).