Есть ли у TCP-сокета «поддерживать активность»?

85

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

Кевин Бойд
источник
1
Чтобы убедиться, что http keepalive обычно не связан с keepalive сокетов, в нем говорится о функции HTTP / 1.1, позволяющей сохранять соединения открытыми для дальнейших запросов. Это связано только с TCP keepalive, так как ему необходимо обнаруживать разорванные TCP-соединения (или обычно держать сокеты открытыми только в течение ограниченного времени).
eckes 05

Ответы:

70

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

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

Мэтью Шарли
источник
4
Это хорошая идея. Вы не обязаны , но если вы этого не сделаете, вы можете не обнаружить неработающую ссылку, пока кто-то действительно не захочет что-то сделать. Что может быть, а может и не быть хорошим (или может иметь или не иметь значения), в зависимости от того, чего вы на самом деле пытаетесь достичь.
Мэтью Шарли,
1
@Pacerier: зависит от протокола, так как он полностью зависит от протокола, но для текстовых протоколов, требующих одного буквального "PING" и "PONG", довольно типичны.
Мэтью Шарли
4
@MatthewScharley: Этот «пинг-понг» уже реализован для нас в стандартных реализациях TCP и называется «keep-alive» (см. Другой популярный ответ на этот вопрос). Есть ли причина реализовать это на уровне приложения?
Тим Купер
7
@TimCooper: На самом деле это не так. Как я подчеркивал в комментариях к другим ответам, реализация TCP бесполезна для большинства требований уровня приложения . Вы не можете отправить его по запросу, и для большинства операционных систем тайм-аут поддержки активности TCP можно настроить только на общесистемном уровне и установить слишком большим, чтобы быть обычно полезным для приложений.
Мэтью Шарли,
14
@Tim Причина поддержания активности на уровне приложения заключается в том, что стандарт TCP рекомендует устанавливать таймер поддержания активности более двух часов. Никогда не видел TCP-соединения без трафика, который выживает на этот раз. Следовательно, протокол TCP keep-alive по умолчанию бесполезен.
Роберт
99

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

Короткий ответ - нет, он не будет оставаться открытым вечно, он, вероятно, отключится через несколько часов. Поэтому да там есть тайм - аут , и это обеспечивается через TCP Keep-Alive .

Если вы хотите настроить тайм-аут Keep-Alive на вашем компьютере, см. Раздел «Изменение тайм-аута TCP» ниже. В противном случае прочитайте остальную часть ответа, чтобы узнать, как работает TCP Keep-Alive.

Введение

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

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

Живой процесс

Есть три настраиваемых свойства, которые определяют, как работает 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 секунд).

Попался

2 часа по умолчанию

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

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

Согласно RFC 1122 4.2.3.6 , отвечать и / или ретранслировать пакеты TCP Keep-Alive необязательно :

Разработчики МОГУТ включать "keep-alive" в свои реализации TCP, хотя эта практика не является общепринятой. Если пакеты keep-alive включены, приложение ДОЛЖНО иметь возможность включать или выключать их для каждого 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, - это lucwilliams / JavaLinuxNet и flonatel / libdontdie .

Кори Кляйн
источник
Очень полезно, спасибо! Одно дополнение: для Windows требуется перезагрузка, чтобы новые значения KeepAliveTime вступили в силу.
geld0r
В AIX текущие настройки TCP Keep-Alive можно запросить с помощью $ no -a | grep tcp_keepкоманды.
Jarek Przygódzki
56

Вам нужен сокет SO_KEEPALIVE.

В API Java Торцевые обнажает «поддержания активности» для приложений через setKeepAliveи getKeepAliveметодов.

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

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

Стивен С
источник
4
Если я setKeepAlive (true); каков будет интервал? ... также будет ли Java продолжать отправлять сообщения keep-alive с интервалом по умолчанию или мне придется делать это программно?
Кевин Бойд,
3
unixguide.net/network/socketfaq/4.7.shtml Имеет описание SO_KEEPALIVE. Это не так много , что ОП хочет, хотя это на основе протокола вариант что я предложил ... хотя, один раз в два часа не будет делать для приложений.
Мэтью Шарли,
4
@MatthewScharley Что касается "не должно быть по умолчанию не менее двух часов" ... значит, разрешено быть менее двух часов, верно?
Pacerier
1
@MatthewScharley - «Вы правы, но это зависит от конкретной реализации ...» . Интервал поддержания активности, который не может быть меньше двух часов, был бы настолько бесполезен, что трудно представить, чтобы кто-нибудь его реализовал.
Stephen C
2
@Daniel - альтернативой (на Java) было бы ручное сохранение активности, как упоминалось выше и в других ответах. Не очень красиво, но, возможно, это лучше, чем изменение значения по умолчанию для всей ОС, которое может нарушить работу системных служб или других приложений.
Stephen C
34

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

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

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

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

ZZ Coder
источник
2
Вы можете изменить интервал поддержки активности TCP в соответствии с вашим приложением. Например, msdn.microsoft.com/en-us/library/dd877220%28VS.85%29.aspx
Дэн Бериндей,
@ZZCoder Не могли бы вы уточнить, что означает, когда вы говорите «В HTTP, keepalive означает постоянное состояние соединения»?
Pacerier
1
@Pacerier: в HTTP/1.0каждом запросе / ответе требовалось переподключение к серверу. Потому что HTTP/1.1они представили Keep-Aliveзаголовок, который можно использовать, чтобы заставить сервер не разрывать соединение после завершения обработки ответа, чтобы облегчить запрос дополнительных файлов и обеспечить «конвейерную обработку»; отправка нескольких запросов и ожидание возврата всех данных.
Мэтью Шарли
Это в основном означает, что многие HTTP-запросы будут / должны повторно использовать одно и то же TCP-соединение (эти соединения также могут иметь keep-alive, но это не соответствует HTTP, поэтому это, по сути, другая концепция).
Игорь Кордаш 04
24

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

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

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

Artelius
источник
Ах! вы добавляете здесь хороший момент, а именно: вы должны также учитывать промежуточные факторы, которые могут препятствовать работе соединения, такие как маршрутизаторы NAT и т. д.
Кевин Бойд,
4
Это хороший момент и хорошее напоминание о том, что нужно иметь в виду не только то, что мы непосредственно реализуем сами. А еще лемминги !!
Мэтью Шарли,
Обратите внимание, что совместное использование файлов p2p и поглощает много портов, и создает множество зомби-соединений, что повышает вероятность того, что NAT потребуется отсечь неактивные соединения.
Artelius,
4
Не обязательно, что TCP-соединение идентифицируется 4 элементами: src ip, src port, dest ip, dest port. Таким образом, вы можете повторно использовать один и тот же внешний (исходный) порт, если IP-адрес назначения отличается.
Дэн Бериндей
1
Ах да, ты прав. Я думаю, настоящая причина в том, что у NAT есть таблица открытых соединений фиксированного размера из-за ограничений памяти и времени поиска.
Artelius
4

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

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

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

Джич
источник
1

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

**** ОС Windows ****

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

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

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

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

ОС Linux

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

int setsockopt (int socket, int level, int optname, const void * optval, socklen_t optlen)

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

Суганья Винаякам
источник
0

Для Windows согласно Microsoft docs

  • 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 и не может быть изменено
сперма
источник