Есть ли в соединении TCP-сокета "keep alive"?

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

8 ответов

Решение

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

Реализаторы МОГУТ включать "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 в реестре по адресу

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

Сноски

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

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

3. Некоторые примеры, которые я нашел в основном поиске Google: https://github.com/lucwillems/JavaLinuxNet и https://github.com/flonatel/libdontdie.

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

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

РЕДАКТИРОВАТЬ: SO_KEEPALIVE реализован в стеках сетевых протоколов ОС без отправки каких-либо "реальных" данных. Интервал поддержания активности зависит от операционной системы и может быть настроен с помощью параметра ядра.

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

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

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

Пакеты поддержки активности ДОЛЖНЫ отправляться только в том случае, если в течение определенного интервала не было получено пакетов данных или подтверждений для соединения. Этот интервал ДОЛЖЕН быть настраиваемым и ДОЛЖЕН по умолчанию составлять не менее двух часов.

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

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

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

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

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

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

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

В JAVA Socket - TCP-соединения управляются на уровне ОС, java.net.Socket не предоставляет встроенной функции для установки тайм-аутов для пакетов keepalive на уровне сокетов. Но мы можем включить опцию keepalive для java-сокета, но это занимает 2 часа 11 минут (7200 секунд) по умолчанию для обработки после устаревших соединений tcp. Эта причина подключения будет доступна в течение очень долгого времени до продувки. Таким образом, мы нашли какое-то решение использовать Java Native Interface (JNI), который вызывает собственный код (C++) для настройки этих параметров.

**** Windows OS ****

В операционной системе Windows можно настроить keepalive_time & keepalive_intvl, но нельзя изменить tcp_keepalive_probes. По умолчанию при инициализации сокета TCP время ожидания остается равным 2 часам, а время ожидания - 1 секунде. Общесистемное значение по умолчанию для тайм-аута keep-alive можно контролировать с помощью параметра реестра KeepAliveTime, который принимает значение в миллисекундах.

В Windows Vista и более поздних версиях количество проверок активности (повторных передач данных) установлено на 10 и не может быть изменено.

В Windows Server 2003, Windows XP и Windows 2000 значение по умолчанию для количества проверок проверки активности по умолчанию равно 5. Количество проверок проверки активности контролируется. Для Windows библиотека IOCTL Winsock используется для настройки параметров tcp-keepalive.

int WSAIoctl (SocketFD, // дескриптор, идентифицирующий сокет SIO_KEEPALIVE_VALS, // dwIoControlCode (LPVOID) lpvInBuffer, // указатель на tcp_keepalive struct (DWORD) cbInBuffer, // длина входного буфера NULL, // размер выходного буфера, // выходной буфер 0, // выходной буфер 0, // выходной буфер 0, // выходной буфер 0, // выходной буфер 0, // выходной буфер 0, // выходной буфер 0, // выходной буфер 0 выходной буфер (LPDWORD) lpcbBytesReturned, // количество возвращаемых байтов NULL, // OVERLAPPED структура NULL // процедура завершения);

ОС Linux

В Linux есть встроенная поддержка keepalive, которая необходима для включения сетей TCP/IP, чтобы использовать ее. Программы должны запрашивать контроль активности для своих сокетов, используя интерфейс setsockopt.

int setsockopt (int сокет, int уровень, int optname, const void * optval, socklen_t optlen)

Каждый клиентский сокет будет создан с использованием java.net.Socket. Идентификатор дескриптора файла для каждого сокета будет получен с использованием java-отражения.

Для Windows в соответствии с документами Microsoft

  • KeepAliveTime (REG_DWORD, миллисекунды, по умолчанию не установлено, что означает 7 200 000 000 = 2 часа) - аналог tcp_keepalive_time
  • KeepAliveInterval (REG_DWORD, миллисекунды, по умолчанию не установлено, что означает 1000 = 1 секунда) - аналог tcp_keepalive_intvl
  • Поскольку в Windows Vista нет аналога tcp_keepalive_probes, значение установлено на 10 и не может быть изменено.
Другие вопросы по тегам