Как использовать свойство TCP keep_alive для получения уведомлений о событии не отвечающего узла?

Сценарий.
У меня есть клиент и сервер, написанные с использованием boost::asio 1.63. В целом связь и коммуникационная часть работают хорошо и отлично.

Я написал Watchdog с обеих сторон, которые отправляют фиктивные пакеты партнерам с интервалом в 2 секунды каждый. Цель сторожевого таймера состоит в том, чтобы заинтересованный одноранговый узел сообщал об ошибке соединения, если он не получил фиктивный пакет, который ожидал в течение следующих 2 секунд. Это еще более важно для меня, потому что может случиться так, что 2 одноранговых узла не передают пакеты для какой-либо пользовательской цели, но каждый из них должен сообщать об ошибке соединения, если какой-либо из одноранговых узлов выходит из строя. Одноранговый узел может выйти из строя даже из-за сбоя ядра, и в этом случае этот одноранговый узел не сможет отправить сообщение. Конечно, это классическая проблема, которая существует даже за пределами asio и TCP.

Мой Watchdog работает отлично. Никаких проблем.

Но недавно я прочитал о keep_alive в сокетах. Я попробовал следующий код и, похоже, я могу использовать свойство keep_alive в сокете TCP, получив собственный дескриптор сокета из моего кода, используя boost::asio.

boost::asio::io_service      ioService;
boost::asio::ip::tcp::socket mySocket(ioService);

int on = 1;
int delay = 120;
setsockopt(mySocket.native_handle(), SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
setsockopt(mySocket.native_handle(), IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay));

Вопрос.
Приведенный выше код хорошо компилируется в macOS, Linux и iOS. Это выглядит великолепно. Но какая мне от этого польза? Дает ли это мне обратный вызов или событие, когда одноранговый узел выходит из строя? Освобождает ли это меня от написания Watchdog, о котором я говорил выше?

Я использовал boost::asio::async_connect для подключения к узлу. Могу ли я получить обратный вызов моему обработчику соединений, когда perr выходит из строя после определенного интервала времени ожидания?

Установив параметры keep_alive, как мне тогда узнать, что мой пир больше не отвечает?


person AdeleGoldberg    schedule 04.11.2019    source источник
comment
Вам не нужны необработанные setsockopt, вы можете использовать mySocket.set_option   -  person tkausl    schedule 04.11.2019
comment
Does this give me a callback or event when the peer goes down? Я предполагаю, что он не работает с кодом выхода при чтении и записи.   -  person tkausl    schedule 04.11.2019
comment
Итак, похоже, мне все еще нужно написать сторожевой таймер. Он не отправит мне событие, если мой одноранговый узел бездействует и не выполняет никаких операций чтения/записи. Я правильно понял?   -  person AdeleGoldberg    schedule 04.11.2019
comment
It wouldn't send me an event if my peer is sitting idle and not doing any read/write. Верно, но когда это произойдет? Обычно у вас всегда есть чтение, ожидающее данных, не так ли?   -  person tkausl    schedule 04.11.2019
comment
Поддержка активности TCP (а) отключена по умолчанию (б) при включении работает по умолчанию с двухчасовыми интервалами (в) вызывает сброс соединения в следующий раз, когда вы используете сокет для ввода-вывода, если обнаружен мертвый узел.   -  person user207421    schedule 04.11.2019
comment
Ага! Вы имеете в виду, что у меня есть бесконечный asyc_read и бесконечный asyc_write с обеих сторон, что я и делаю :-). Тогда asyc_read/asyc_write вернется с ошибкой, как только будет обнаружено, что одноранговый узел не работает? Не могли бы вы подтвердить? Если это так, то я счастлив и попробую это на своем коде, чтобы увидеть, работает ли он. Вы можете опубликовать это как ответ, если это действительно так.   -  person AdeleGoldberg    schedule 04.11.2019
comment
Correct, but when would this happen? You'd usually always have a read waiting for data, no? Да. Я делаю. У меня есть async_read, всегда ожидающий данных. Я должен был сделать что-то неправильно в значениях времени ожидания. Тогда я должен получить обратный вызов с ошибкой на моем async_read?   -  person AdeleGoldberg    schedule 04.11.2019
comment
Спасибо всем. У меня это работает. Сейчас он находится в стадии тестирования. У меня есть последний вопрос по адресу stackoverflow.com/questions/58696319/, если у кого-то из вас, ребята, есть предложение.   -  person AdeleGoldberg    schedule 04.11.2019
comment
@AdeleGoldberg Мне интересно, как ты заставил это работать. Не могли бы вы написать (очень) краткое резюме в виде самостоятельного ответа?   -  person sehe    schedule 05.11.2019


Ответы (1)


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

Проблема в том, что опция TCP keep_alive не всегда обнаруживает разъединения.

Как правило, нет надежного способа обнаружить внезапное отключение, кроме как путем реализации ping/heartbeat на уровне приложения.

Вы также можете просмотреть эту ветку.

person Igor R.    schedule 13.11.2019
comment
Поддержание активности TCP всегда обнаруживает отключение. Проблема в том, что по умолчанию его таймер установлен на 2 часа бездействия, после чего он отправляет зонд. Вы можете настроить таймер на гораздо более низкое значение. - person Maxim Egorushkin; 13.11.2019
comment
@Максим Егорушкин, насколько я помню, я пытался сделать это (на винде), и это все равно не охватило все сценарии. Даже если это так, настройка общесистемного параметра кажется неоптимальным решением. - person Igor R.; 13.11.2019
comment
Тайм-аут — это параметр для каждого сокета в Linux, не уверен в Windows. Подробнее. - person Maxim Egorushkin; 13.11.2019
comment
Я пробовал tcp keep_alive на macos, android и ios. Кажется, он обнаруживает все разъединения для меня. Я проверил это довольно строго. Я думаю, что это работает для меня. - person AdeleGoldberg; 18.11.2019
comment
@AdeleGoldberg, даже если ты перережешь кабель? - person Igor R.; 18.11.2019
comment
@ИгорьР. Я не пробовал перерезать кабель :-) Но если я отключу Wi-Fi с одной стороны, то обе стороны обязательно обнаружат потерю соединения. Или, например, если я отключу устройство ios, другая сторона обнаружит потерю соединения. Что еще я могу попробовать? - person AdeleGoldberg; 18.11.2019
comment
@AdeleGoldberg хорошо, изящное завершение работы закроет сокеты. У меня был неудачный опыт работы с keep-alive на некоторых платформах (кажется, на Windows), когда одноранговый узел внезапно отключался или выдергивался кабель. - person Igor R.; 18.11.2019
comment
Я не тестирую на окнах. Но я тестировал такие сценарии, как killall -9 MyApp или pkill MyApp на стороне Linux. Это также хорошо сработало для keep_alive, чтобы обнаружить поломку. - person AdeleGoldberg; 18.11.2019