LINUX.ORG.RU
Форум — Admin  

Как работает Keepalive?

 , keepalive,


0

2

В очередной раз столкнувшись с зависанием табов Firefox, когда перестаёт работать клавиатура, я узнал от Гугло-ИИ про «молчаливое бесконечное зависание сокетов» и сообщения KeepAlive. Поискал информацию в других местах, и вопросов стало ещё больше:

  1. Правильно ли я понимаю, что
    1.1. при плохой связи или намеренном удушении трафика до моей машины не доходят сообщения об обрыве связи,
    1.2. поэтому программа может вечно висеть и считать себя работающей, ничего не скачивая/не передавая,
    1.3. поэтому придумали отправку сигнала KeepAlive,
    1.4. и при плохой связи имеет смысл увеличить частоту запросов и уменьшить время до нескольких минут?

  2. Если по умолчанию tcp_keepalive_time — 2 часа, а затем 9 (tcp_keepalive_probes) попыток с интервалом 75 с (tcp_keepalive_intvl), почему curl, браузеры, FTP-сервера и другие программы могут висеть, ничего не скачивая, гораздо дольше 2 часов 12 минут? Считать ли способность не работать и не выдавать ошибку дольше 2 часов 12 минут багом сетевой программы?

  3. Где об этом стоит прочитать? Помимо man sysctl, man sysctl.conf и man tcp.

  4. Можно ли глобально прибить все подвисшие соединения на уровне ядра?

  5. Пытался ли кто-нибудь решить проблему мёртвого зависания при плохой связи глобально? Можно ли, например, при таймауте на уровне TCP долго пытаться восстановить связь, а не рвать окончательно?

  6. Можно ли настраивать параметры keepalive для браузеров в Андроиде без рута?

★★★★★

Последнее исправление: question4 (всего исправлений: 1)

Обычно такие вещи тюнят через setsockopt на конкретном сокете. Глобальные параметры у ядра есть, но, насколько я понимаю, они применяются только к сокетам, для которых включён SO_KEEPALIVE - что в браузере маловероятно, так как там почти всё stateless и большинство проблем можно решить релоудом страницы

annulen ★★★★★
()

Можно ли глобально прибить все подвисшие соединения на уровне ядра

Проблема в том, что надёжно определить, какое соединение на самом деле «подвисшее», можно только отправив по нему что либо и получив ответ. Это может сделать приложение, но не ядро. Кстати, именно поэтому в большинстве протоколов, работающих поверх TCP и требующих долгоживущих соединений, предусматривается свой собственный keepalive-обмен (периодические «пинги»)

annulen ★★★★★
()

tcp-проблемы никак с зависанием табов не связаны, если что. Табы виснут из-за js и иногда из-за багов браузера.

висеть, ничего не скачивая

А это может быть просто потому, что сервер ничего не шлёт. При этом соединение вполне может быть в норме. Ну и баг браузера тоже может к этому приводить, куда уж без него.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)

почему curl, браузеры, FTP-сервера и другие программы могут висеть, ничего не скачивая, гораздо дольше 2 часов 12 минут

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

не доходят сообщения об обрыве связи

Не существует сообщений об обрыве связи, только таймауты. Разве что само ядро пришлёт сигнал приложению каким-нибудь connection reset.

при плохой связи имеет смысл увеличить частоту запросов

Скорее наоборот. Связь может сама собой восстановиться. А вот если постоянно меняются IP адреса, или быстро протухает NAT кэш в роутере, то да, надо почаще.

Можно ли глобально прибить все подвисшие соединения на уровне ядра?

Прислать им reset через ядро и raw packets.

Можно ли, например, при таймауте на уровне TCP долго пытаться восстановить связь, а не рвать окончательно?

Можно, но обычно проще и лучше новое соединение поднять. Если нужна максимальная отзывчивость, то вообще надо пул соединений поднимать.

Можно ли настраивать параметры keepalive для браузеров

RFC1122: If keep-alives are included, the application MUST be able to turn them on or off for each TCP connection, and they MUST default to off.

Скорее всего нельзя.

neumond ★★
()

tcp-keepalive пакеты решают вот какие две задачи (и только их):

1. Если на том конце уже точно знают, что соединение разорвано (точнее, решили что надо его разорвать), а на этом - ещё почему-то нет, то когда ОС шлёт туда очередной keepalive, та сторона, скорее всего, ответит нам, что такого соединения она не знает, и на нашей стороне соединение тоже станет закрытым. Если же ничего не слать, то мы не узнаем об этом никогда (впрочем, наша программа может по собственной инициативе закрыть соединение исходя из каких-то своих причин, но это уже другая тема). Тут стоит сделать два уточнения

1.а. Та сторона может ничего и не ответить на этот keepalive в зависимости от настроек, и тогда мы о разрыве соединения всё равно не узнаем. Но так настраивают редко.

1.б. Слать не обязательно keep-alive, если программа сама шлёт какие-то байты в сокет, то keep-alive становится не нужен. Но слать или не слать байты в сокет - решение программы, а keep-alive можно послать всегда, на уровне ОС, он не связан с передачей полезных данных. Отсылка байтов в сокет более эффективна для распознавания разрывов, так как (см. п.1.а) keep-alive в случае работающего соединения ответа не требует, а в случае оборванного та сторона всё равно может молча его проигнорировать, или та сторона может вообще отключиться от интернета и не ответить, и мы ничего с этим не сделаем. А вот с отправкой байтов в сокет ситуация другая: в них отсутствие ответа за разумное время будет считать разрывом соединения уже на нашей стороне.

2. Если соединение идёт не напрямую, а через NAT или несколько NAT-ов, то эти NAT-ы хранят списки всех активных идущих через них соединений. Многие, в целях оптимизации расходов своих вычислительных мощностей, нарушают tcp-протокол и выкидывают из таблиц информацию о соединениях, через которые давно не было пакетов. При этом на самом деле обе стороны соединения вполне могут считать соединение живым, но просто пока что не используемым. А вот если иногда слать keepalive то это будет напоминанием NAT-у, что соединение нельзя выкидывать.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

1.а. Та сторона может ничего и не ответить на этот keepalive в зависимости от настроек, и тогда мы о разрыве соединения всё равно не узнаем. Но так настраивают редко.

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

lonelywoolf
()
Ответ на: комментарий от lonelywoolf

На keepalive в норме ответа как раз не приходит, я ж написал это ниже. Ответ требуется только когда в пакете есть данные (та сторона должна обновить ACK в соответствии с ними), а keepalive это пакет в один конец, с надеждой (но не гарантией) что нам сообщат если его невозможно доставить (тогда закрываем).

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от lonelywoolf

Уровни OSI тут ни при чём, в tcp-keep-alive пакете нет данных и соответственно ack на него не делается. Вообще, keep-alive это по сути и есть ack, отправленный от нас удалённому узлу, дубль последнего ранее отправленного.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Ты мне щас хочешь сказать, что удаленный хост не отвечает ACK?

Some TCP implementations, however, have included a keep-alive mechanism. To confirm that an idle connection is still active, these implementations send a probe segment designed to elicit a response from the peer TCP. Such a segment generally contains SEG.SEQ = SND.NXT-1 and may or may not contain one garbage octet of data. Note that on a quiet connection SND.NXT = RCV.NXT, so that this SEG.SEQ will be outside the window. Therefore, the probe causes the receiver to return an acknowledgment segment, confirming that the connection is still live. If the peer has dropped the connection due to a network partition or a crash, it will respond with a RST instead of an acknowledgment segment.

RFC 1122.

lonelywoolf
()
Ответ на: комментарий от firkax

Ты бы лучше RFC бы открыл. Я тебе даже ссылочку привёл. На твои фантазии так то начхать. Может быть, ты всё же надумаешь вернуться в конструктивное русло и процитируешь из стандарта, где ответный ACK на Probe не должен приходить?

lonelywoolf
()
Ответ на: комментарий от firkax

tcp-проблемы никак с зависанием табов не связаны, если что. Табы виснут из-за js и иногда из-за багов браузера.

Трудновоспроизводимый баг Firefox, который проявляется только при низком качестве связи. Смутно припоминаю его в Netscape 4, в начале 2000-х, регулярно сталкивался в 2011 году в метро с модема.

question4 ★★★★★
() автор топика
Ответ на: комментарий от no-such-file

Ты можешь считать что угодно, но линуксовый (и не только) tcp-стек работает как написал я. И почему оно именно так работает я тоже уже объяснял. Впрочем, если ты, кажется, не знаешь что такое tcp ack, то и объяснения не понял.

Но если не веришь - можешь поставить keepalive_time и keepalive_intvl в 10 секунд (чтобы они быстрее начались), запустить tcpdump на некий ip:port и подключиться туда телнетом, подождать 10 сек и смотреть какие куда пакеты ходят.

firkax ★★★★★
()
Ответ на: комментарий от firkax

смотреть какие куда пакеты ходят

Очевидно, что никакие никуда, т.к. keep-alive нужно отдельно включать, телнет этого не делает.

линуксовый (и не только) tcp-стек работает как написал я

Все стеки работают плюс-минус одинаково. Единственный момент, что некоторые древние/тупые реализации рассчитывают что в пакете будет 1 мусорный байт, и если данных нет то такой пакет тупо игнорится (всё как ты рассказываешь). Но во-первых линукс так не делал никогда. Во-вторых это вызывает проблемы, т.к. без ACK пинг считается неуспешным в любом случае и соединение закрывается после нескольких попыток.

no-such-file ★★★★★
()
Ответ на: комментарий от firkax

Ну не совсем точно.
KEEPALIVE посылает ACK без данных, но при это SEQ на 1 меньше подтвердила удаленная сторона. В ответ удаленная сторона присывает ACK с текущим SEQ.

vel ★★★★★
()
Ответ на: комментарий от question4

Смутно припоминаю его в Netscape 4, в начале 2000-х

Активно пользовал нетшкаф, но такого не припоминаю.

anc ★★★★★
()
Ответ на: комментарий от vel

Так, ладно, признаю свою неправоту, это действительно так. А до этого я невнимательно проверял, увы.

no-such-file, да, ты оказался прав. Впрочем, закрывание соединения при отсутствии ответа я не проверял, но наверно оно там и правда есть, иначе бы незачем было смещать seq.

lonelywoolf - ты тоже

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария