Подтвердить что ты не робот

У вас какой-либо boost:: asio async вызывает автоматический тайм-аут?

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

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

Например:

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

  • Для async_connect в syscall connect(2) есть встроенный в него тайм-аут

  • и др.

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

4b9b3361

Ответ 1

Итак, я сделал несколько тестов. Основываясь на моих результатах, ясно, что они зависят от базовой реализации ОС. Для справки, я протестировал это с помощью ядра 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 секунд с двумя попытками (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, соединение было немедленно закрыто, но я не знал этого, пока не попытаюсь выполнить следующую операцию (которая сразу сообщит 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(). Когда он вызывается, ошибка установлена ​​на boost::asio::error::timed_out.


async_read()

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

Ответ 2

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