Чи має підключення до сокета TCP функцію "підтримувати живу"?


84

Я чув про HTTP Keep-Live, але наразі я хочу відкрити сокетне з'єднання з віддаленим сервером.
Тепер чи залишатиметься це сокетне з’єднання назавжди відкритим, чи з ним пов’язане обмеження часу, подібне до HTTP Keep-Live?


1
Тільки для того, щоб переконатись, що "http keepalive", як правило, не пов'язаний із socket keepalive, тут йдеться про функцію HTTP / 1.1 зберігати з'єднання відкритими для подальших запитів. Це пов’язано лише з підтримкою TCP, оскільки йому потрібно виявляти пошкоджені з'єднання TCP (або, як правило, тримає сокети відкритими протягом обмеженого часу).
eckes

Відповіді:


72

TCP-сокети залишаються відкритими до закриття.

Тим не менш, дуже важко виявити пошкоджене з’єднання (пошкоджене, як у маршрутизаторі, і т. Д., На відміну від закритого) без фактичної надсилання даних, тому більшість додатків проводять певні реакції пінг-понгу так часто, щоб просто переконатися зв'язок все ще живе.


4
Це гарна ідея. Вам не потрібно , але якщо цього не зробити, ви можете не виявити непрацююче посилання, поки хтось насправді не захоче щось зробити. Що може бути, а може і не добре (а може і не мати значення), залежно від того, чого ви насправді намагаєтесь досягти.
Matthew Scharley

1
@Pacerier: Залежить від протоколу, оскільки він повністю залежить від протоколу, але для текстових протоколів, для яких потрібні одні буквальні команди "PING" і "PONG", досить типові.
Метью Шарлі,

4
@MatthewScharley: Цей "пінг-понг" вже реалізований для нас, у стандартних реалізаціях TCP, і називається "Keep-Live" (див. Іншу популярну відповідь на це питання). Чи є якісь причини застосовувати це на рівні додатка?
Тім Купер

7
@TimCooper: Це не так. Як я підкреслював у коментарях до інших відповідей, реалізація TCP не є корисною для більшості вимог рівня додатків . Ви не можете надіслати його на вимогу, і для більшості операційних систем тайм-аут TCP Keepalive може бути налаштований лише на загальносистемному рівні та встановлений занадто високо, щоб бути загалом корисним для додатків.
Matthew Scharley

13
@Tim Причиною збереження активності на рівні програми є те, що стандарт TCP рекомендує встановити таймер підтримання активності на рівні понад дві години. Ніколи не бачив TCP-з'єднання без трафіку, який переживає цей час. Отже, матеріали TCP, що підтримують життя, за замовчуванням марні.
Роберт,

97

Тепер чи залишатиметься це сокетне з’єднання назавжди відкритим, чи з ним пов’язане обмеження часу очікування, подібне до HTTP Keep-Live?

Коротка відповідь: ні, це не залишиться відкритим назавжди, можливо, через кілька годин очікується час. Тому так , є час очікування, і він застосовується через 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. Якщо в цей момент відповіді не отримано, надішліть a RSTта розірвіть з'єднання.
    • Так : Поверніться до кроку 2

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

Маю

2 години за замовчуванням

Оскільки процес не починається, поки зв’язок за промовчанням не простояв дві години, застарілі з’єднання TCP можуть затриматись дуже довго, перш ніж бути обрізаними. Це може бути особливо шкідливим для дорогих підключень, таких як підключення до бази даних.

Keep-Alive є необов’язковим

Відповідно до RFC 1122 4.2.3.6 , відповідати та / або передавати пакети TCP Keep-Alive необов’язково :

Реалізатори МОЖУТЬ включити "живих" до своїх реалізацій TCP, хоча ця практика не є загальновизнаною. Якщо включені програми збереження, програма ПОВИННА мати можливість їх увімкнути або вимкнути для кожного 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

55

Ви шукаєте варіант розетки SO_KEEPALIVE.

В API Java Торцеві оголює «підтримки активності» для додатків через setKeepAliveтаgetKeepAlive методів.

EDIT: SO_KEEPALIVE реалізовано в стеках мережевих протоколів ОС без надсилання будь-яких "реальних" даних. Інтервал збереження життя залежить від операційної системи і може бути налаштований за допомогою параметра ядра.

Оскільки дані не надсилаються, SO_KEEPALIVE може протестувати лише активність мережевого підключення, а не активність служби, до якої підключено сокет. Щоб протестувати останнє, потрібно реалізувати щось, що передбачає надсилання повідомлень на сервер та отримання відповіді.


4
Якщо я setKeepAlive (true); який би був інтервал? ... також чи продовжуватиме Java надсилати повідомлення про підтримку через інтервал за замовчуванням, чи мені доведеться робити це програмно?
Кевін Бойд,

3
unixguide.net/network/socketfaq/4.7.shtml Має опис SO_KEEPALIVE. Це не так багато , що ОП хоче, хоча це на основі протоколу варіант що я запропонував ... хоча, один раз в двоє години буде робити для додатків.
Matthew Scharley

4
@MatthewScharley Щодо "він не повинен за замовчуванням мати не менше двох годин" ... означає, що це може бути менше двох годин, правда?
Pacerier

1
@MatthewScharley - "Ви маєте рацію, але це стосується реалізації ..." . Інтервал збереження життя, який не може бути менше двох годин, був би настільки марним, що важко уявити, щоб хтось його реалізовував.
Stephen C

2
@Daniel - альтернативою (на Java) буде ручне збереження життя, як згадувалося вище та в інших відповідях. Не гарно, але це може бути краще, ніж зміна за замовчуванням для всієї ОС, яка може зламати системні служби чи інші програми.
Stephen C

34

TCP Keepalive та HTTP Keepalive - це дуже різні поняття. У TCP keepalive - це адміністративний пакет, що надсилається для виявлення застарілого з'єднання. У HTTP keepalive означає постійний стан з'єднання.

Це із специфікації TCP,

Пакети збереження живлення ПОВИННІ надсилати лише тоді, коли за з'єднання протягом певного інтервалу не було отримано пакетів даних або підтверджень. Цей інтервал ПОВИНЕН налаштовуватись і ПОВИНЕН за замовчуванням становити не менше двох годин.

Як бачите, інтервал збереження TCP за замовчуванням завеликий для більшості програм. Можливо, вам доведеться додати keepalive у ваш протокол програми.


2
Ви можете змінити інтервал збереження TCP відповідно до вашої програми. Наприклад, msdn.microsoft.com/en-us/library/dd877220%28VS.85%29.aspx
Dan Berindei,

@ZZCoder Чи можете ви детальніше пояснити, що це означає, коли ви говорите "У HTTP keepalive означає постійний стан з'єднання"?
Pacerier

1
@Pacerier: У HTTP/1.0кожному запиті / відповіді необхідне повторне підключення до сервера. Оскільки HTTP/1.1вони ввели Keep-Aliveзаголовок, який можна використовувати для запуску сервера, щоб не перервати з'єднання після обробки відповіді, щоб полегшити запит на додаткові файли та дозволити "конвеєризацію"; надсилання декількох запитів, а потім очікування повернення всіх даних.
Метью Шарлі

Це в основному означає, що багато запитів HTTP будуть / повинні використовувати одне і те ж TCP-з'єднання (ці з'єднання можуть також мати підтримку в реальному житті, але це не відповідає HTTP, тому це, по суті, інша концепція).
Ігор Чордаш

24

Якщо ви стоїте за маскувальним NAT (як і в наш час більшість домашніх користувачів), існує обмежений пул зовнішніх портів, і вони повинні бути спільними між TCP-з'єднаннями. Тому маскуючі NAT, як правило, вважають, що з'єднання було розірвано, якщо протягом певного періоду часу не було надіслано даних.

Ця та інші подібні проблеми (в будь-якому місці між двома кінцевими точками) можуть означати, що з’єднання більше не буде «працювати», якщо ви спробуєте надіслати дані після неробочого періоду простою. Однак ви можете не виявити цього, поки не спробуєте надіслати дані.

Використання програм Keepalives одночасно зменшує ймовірність переривання зв’язку десь по лінії, а також дозволяє швидше дізнатися про розірваний зв’язок.


Ах! ви додаєте тут хороший момент, тобто ви також повинні враховувати проміжки, які можуть перешкоджати роботі з’єднання, наприклад, маршрутизатори NAT тощо ...
Кевін Бойд

4
Це хороший момент і гарне нагадування про те, що слід пам’ятати не лише про те, що ми безпосередньо реалізуємо самі. Крім того, Лемінги !!
Matthew Scharley

Зверніть увагу, що спільний доступ до файлів p2p пережовує велику кількість портів і створює безліч зомбі-з'єднань, що робить більш імовірним, що NAT буде потрібно обрізати простої з'єднань.
Артелій

4
Необов’язково, TCP-з'єднання визначається за допомогою 4 елементів: src ip, порт src, dest ip, dest порт. Таким чином, ви можете повторно використовувати той самий зовнішній (вихідний) порт, доки IP-адреса призначення відрізняється.
Dan Berindei

1
О так, ти маєш рацію. Я думаю, що справжня причина полягає в тому, що NAT мають таблицю відкритих з'єднань фіксованого розміру через обмеження пам'яті та час пошуку.
Артелій

4

Ось додаткова література про Keepalive, яка пояснює це набагато детальніше.

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

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


1

У JAVA Socket - TCP-з'єднання управляються на рівні ОС, java.net.Socket не надає жодної вбудованої функції для встановлення тайм-аутів для керуючого пакету на рівні кожного сокета. Але ми можемо ввімкнути опцію keepalive для сокета java, але за замовчуванням для обробки після застарілих з'єднань tcp потрібно 2 години 11 хвилин (7200 сек). Ця причина буде доступна дуже довго перед очищенням. Тож ми знайшли якесь рішення для використання власного інтерфейсу Java (JNI), який викликає власний код (c ++) для налаштування цих параметрів.

**** ОС Windows ****

В операційній системі Windows keepalive_time і keepalive_intvl можуть бути налаштовані, але tcp_keepalive_probes не можуть бути змінені. За замовчуванням, коли ініціалізовано сокет TCP, встановлюється час очікування до 2 годин, а інтервал до 1 секунди. Загальносистемне значення тайм-ауту Keep-Live контролюється за допомогою параметра реєстру 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 (DWORD) cbInBuffer, // довжина вхідного буфера NULL, // вихідний буфер NULL, // вихідний буфер вихідний буфер (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.


0

Для 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 і не може бути змінено
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.