У каких-нибудь асинхронных вызовов boost :: asio автоматически заканчивается время ожидания? - PullRequest
19 голосов
/ 07 февраля 2011

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

Первоначально я думал, что всякий раз, когда я вызываю функцию async_, я также должен начинать истечение deadline_timer после того, как я ожидаю завершения асинхронной операции. Теперь мне интересно, если это строго необходимо в каждом случае.

Например:

  • async_resolve предположительно использует преобразователь системы, в который встроены тайм-ауты (например, RES_TIMEOUT в resolv.h, возможно, переопределенный конфигурацией в /etc/resolv.conf). Добавляя свой таймер, я могу конфликтовать с тем, как пользователь хочет, чтобы его распознаватель работал.

  • Для async_connect в системный вызов connect(2) встроено какое-то время ожидания

  • и т.д.

Так, какие (если таковые имеются) async_ вызовы гарантированно будут вызывать их обработчики в "разумные" временные рамки? И если тайм-аут операции [может | делает], обработчику будет передана ошибка basic_errors::timed_out или что-то еще?

Ответы [ 2 ]

32 голосов
/ 09 февраля 2011

Итак, я провел некоторое тестирование.Исходя из моих результатов, ясно, что они зависят от базовой реализации ОС.Для справки, я проверил это на стандартном ядре Fedora: 2.6.35.10-74.fc14.x86_64.

Суть в том, что async_resolve() выглядит как единственный случай, когда вы можете быть в состоянии уйтибез установки deadline_timer.Практически во всех остальных случаях это требуется для разумного поведения.


async_resolve()

Вызов async_resolve() привел к 4 запросам с интервалом в 5 секунд.Обработчик вызывался через 20 секунд после запроса с ошибкой boost::asio::error::host_not_found.

Мой распознаватель по умолчанию имеет тайм-аут 5 секунд с 2 попытками (resolv.h), поэтому он, кажется, отправляет вдвое больше запросовсконфигурировано.Поведение можно изменить, установив options timeout и options attempts в /etc/resolv.conf.В каждом случае число отправленных запросов было удвоено независимо от того, на что было установлено значение attempts, а затем вызывался обработчик с ошибкой host_not_found.

Для теста один сконфигурированный сервер имен был перенаправлен в «черную дыру».


async_connect()

При вызове async_connect() с адресом, перенаправленным в черную дыру, обработчик вызывается с ошибкой boost::asio::error::timed_out после ~189 секунд.

Стек отправил первоначальный SYN и 5 повторных попыток.Первая повторная попытка была отправлена ​​через 3 секунды, причем время повторной попытки удваивалось каждый раз (3 + 6 + 12 + 24 + 48 + 96 = 189).Количество попыток может быть изменено:

% sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 5

Значение по умолчанию 5 выбрано в соответствии с RFC 1122 (4.2.3.5):

[таймеры повторной передачи] дляСегмент SYN ДОЛЖЕН быть установлен достаточно большим, чтобы обеспечить повторную передачу сегмента в течение не менее 3 минут.Конечно, приложение может закрыть соединение (т. Е. Отказаться от попытки открытия) раньше.

3 минуты = 180 секунд, хотя в RFC не указывается верхняя граница.Ничто не мешает реализации повторять попытки навсегда.


async_write()

Пока буфер отправки сокета не был заполнен, этот обработчик всегда вызывалсясразу.

Мой тест установил TCP-соединение и установил таймер для вызова async_write() минуту спустя.В течение минуты, когда было установлено соединение, но до вызова async_write(), я попробовал всевозможные беспорядки:

  • Настройка нисходящего маршрутизатора для черной дыры последующего трафика к месту назначения.
  • Очистка сеанса в нисходящем межсетевом экране, чтобы он отвечал поддельными RST из пункта назначения.
  • Отключение моего Ethernet
  • Работает /etc/init.d/network stop

НетЧто бы я ни делал, следующий async_write() немедленно вызывал бы его обработчик, чтобы сообщить об успешном выполнении.

В случае, когда брандмауэр подделывал RST, соединение было немедленно закрыто, но я не мог знать, что доЯ попытался выполнить операцию next (которая немедленно сообщит boost::asio::error::connection_reset).В других случаях соединение будет оставаться открытым и не будет сообщать мне об ошибках до тех пор, пока не истечет время ожидания через 17-18 минут.

Наихудший случай для async_write() - это если хост выполняет повторную передачу и буфер отправкиполный.Если буфер заполнен, async_write() не будет вызывать его обработчик, пока не истечет время повторной передачи.Linux по умолчанию использует 15 повторных передач:

% sysctl net.ipv4.tcp_retries2
net.ipv4.tcp_retries2 = 15

Время между повторными передачами увеличивается после каждой (и основано на многих факторах, таких как предполагаемое время прохождения в оба конца конкретного соединения), но ограничено 2 минутами.Таким образом, с 15 повторными передачами по умолчанию и 2-минутным таймаутом в худшем случае верхняя граница составляет 30 минут для вызова обработчика async_write().При вызове error устанавливается на boost::asio::error::timed_out.


async_read()

Это никогда не должно вызывать его обработчик, пока установлено соединениеи данные не получены.У меня не было времени проверить это.

10 голосов
/ 07 февраля 2011

Эти два вызова МОГУТ иметь тайм-ауты, которые распространяются на ваших обработчиков, но вы можете быть удивлены тем временем, которое требуется до того, как истечет любой из этих тайм-аутов. (Я знаю, что позволил соединению просто сидеть и пытаться соединиться по одному соединению в течение более 10 минут с помощью boost::asio перед тем, как завершить процесс). Также вызовы async_read и async_write не имеют тайм-аутов, связанных с ними, поэтому, если вы хотите, чтобы тайм-ауты были для ваших операций чтения и записи, вам все равно понадобится deadline_timer.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...