Выполняет ли какой-либо boost :: asio async вызов автоматического тайм-аута?

17

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

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

Например:

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

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

  • <р> и др.

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

    
задан eater 07.02.2011 в 19:57
источник

2 ответа

29

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

async_read()

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

    
ответ дан eater 09.02.2011 в 06:27
источник
10

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

    
ответ дан diverscuba23 07.02.2011 в 20:07
источник