Ответ 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()
Это никогда не должно вызывать его обработчик до тех пор, пока соединение установлено и данные не будут получены. Я не успел его протестировать.