Основой тайм-аутов и повторных передач TCP является расчет времени возврата (RTT - round-trip time), соответствующего данному соединению. Мы ожидаем, что оно может изменяться со временем, так как может измениться маршрут, или загрузка сети. TCP должен отследить эти изменения и соответственно модифицировать тайм-ауты.
Во-первых, TCP должен рассчитать RTT между отправкой байта с конкретным номером последовательности и получением подтверждения на этот номер последовательности. Из предыдущей главы мы знаем, что обычно не существует полного соответствия между сегментами данных и подтверждениями (ACK). На рисунке 20.1 это означало, что один RTT, который может быть вычислен отправителем, является временем между передачей сегмента 4 (байты данных 1-1024) и получением сегмента 7 (ACK байт 1-2048), даже если этот ACK подтверждает дополнительно 1024 байта. Мы используем величину M, чтобы обозначить рассчитанный RTT.
Для определения RTT существует расширение к исходной спецификации TCP, которое называется хэшированная оценочная функция RTT (обозначается R)
R ¬ a R + (1 - a )M
где a это коэффициент хэширования с рекомендуемым значением 0,9. Хэширование - способ организации структур данных (хэш таблиц), обеспечивающий эффективный поиск и пополнение; положение элемента данных в хэш таблице определяется значением функции расстановки, отображающей множество возможных ключей элементов данных и множество индексов таблицы и обеспечивающей равномерное заполнение. Хэшированный RTT обновлялся каждый раз, когда осуществлялось новое вычисление. Девяносто процентов данных для каждого нового расчета берется из предыдущего расчета, а десять из нового.
Для подобной хэшированной оценочной функции, которая изменяется с изменением RTT, RFC 793 рекомендует, чтобы тайм-аут повторной передачи (RTO) устанавливался следующим образом
RTO = Rb
где b это коэффициент изменения задержки с рекомендуемым значением равным 2.
[Jacobson 1988] подробно обсуждает проблемы, связанные с подобным подходом, в основном заключающиеся в том, что он не может применяться при широком диапазоне изменения RTT, и является причиной нежелательных повторных передач. Как замечает Jacobson, нежелательные повторные передачи увеличивают загрузку сети.
В этом случае необходимо добавить возможность отслеживать расхождения в расчетах RTT в дополнение к хэшированной функции оценки RTT. Расчет RTO основанный на среднем и расхождении дает значительно лучшие результаты при широком диапазоне изменений времен возврата, чем просто расчет RTO как постоянного кратного среднего значения. Рисунки 5 и 6 в [Jacobson 1988] показывают сравнение значений RTO в соответствии с RFC 793 для некоторых реальных времен возврата, с расчетами RTO, которые мы показали ранее, которые принимают во внимание изменение времен возврата.
Как описано у Jacobson, среднее отклонение является хорошим приближением к стандартному отклонению, однако рассчитывается значительно легче. (Расчет стандартного отклонения требует вычисления квадратного корня.) Таким образом, следующие уравнения могут быть применены к каждому расчету RTT M.
Err = M - A
A ¬ A + gErr
D ¬ D + h(| Err | - D)
RTO = A + 4D
где A это хэшированный RTT (оценочная функция среднего), а D это хэшированное среднее отклонение. Err это разница между только что рассчитанным значением и текущим значением оценочной функции RTT. Оба A и D используются для расчета следующего тайм-аута повторной передачи (RTO). Увеличение среднего (g) установлено в значение 1/8 (0,125). Увеличение отклонения (h) установлено в 0,25. Максимальное увеличение отклонения делает рост RTO быстрее при изменении RTT.
[Jacobson 1988] устанавливает 2D при расчете RTO, однако для следующих исследований [Jacobson 1990c] изменяет это значение на 4D, что соответствует реализации BSD Net/1.
Jacobson показывает способ осуществить эти вычисления с использованием целочисленной арифметики, именно так это обычно делается в стандартных реализациях. (Одна из причин заключается в том, что g, h и множитель 4 - это степени двух, поэтому все операции могут быть осуществлены с помощью сдвига, без умножений и делений.)
Сравнение исходного метода с методом Jacobsonа показывает, что расчеты хэшированного среднего одинаковы (a равно единица минус увеличение (g)), однако используются различные увеличения. Также, расчет RTO по Jacobsonу зависит от обоих значений - хэшированного RTT и хэшированного среднего отклонения, тогда как оригинальный метод использует умножение хэшированных RTT.
В следующем разделе мы увидим, как устанавливаются эти оценочные функции, когда будем рассматривать примеры.
Три большие фигурные скобки, находящиеся с левой стороны временной диаграммы, указывают на то, какие сегменты были использованы при расчете RTT. Не для всех сегментов время было засечено.
Большинство реализаций TCP, происходящих от Berkeley, рассчитывают только одно значение RTT для соединения за один раз. Если в тот момент, когда отправляется сегмент данных, таймер для данного соединения уже используется, время для этого сегмента не засекается.
Установка времени осуществляется путем увеличения счетчика каждый раз, когда запускается 500-миллисекундный таймер TCP. Это означает, что для сегмента, подтверждение на который прибывают через 550 миллисекунд после того, как сегмент был отправлен, может быть принято RTT как равное одному тику (500 миллисекунд), так и RTT равное двум тикам (1000 миллисекунд).
В дополнение к этому счетчику тиков для каждого соединения, запоминается начальный номер последовательности данных в сегменте. Когда принимается подтверждение, содержащее этот номер последовательности, таймер выключается. Если данные не были повторно переданы, когда прибыл ACK, хэшированное RTT и хэшированное среднее отклонение обновляется на основе новых значений.
Таймер для соединения, показанного на рисунке 21.2, стартует, когда передается сегмент 1, и выключается, когда прибывает подтверждение на него (сегмент 2). Несмотря на то, что его RTT равен 1,061 секунды (из вывода команды tcpdump), исследование отладочной информации сокета показывает, что за этот период произошло 3 тика часов TCP, а это обозначает, что RTT равен 1500 миллисекунд.
Следующий сегмент, для которого засекли время, сегмент номер 3. Когда, через 2,4 миллисекунды, передается сегмент номер 4, он не может быть отслежен по времени, так как таймер для этого соединения уже используется. Когда прибывает сегмент 5, подтверждая данные, на которые было засечено время, его RTT рассчитывается равным 1 тику (500 миллисекунд), даже несмотря на то, что, как мы видели из вывода команды tcpdump, его RTT равен 0,808 секунды.
Таймер стартует снова, когда передается сегмент 6, и выключается, когда прибывает подтверждение на него, через 1,015 секунды (сегмент 10). Полученный RTT равен 2 тикам часов. Сегменты 7 и 9 не могут быть оценены по времени, так как таймер занят. Также, когда принимается сегмент 9 (ACK 769), ничего не обновляется, так как подтверждение не подтверждает байты, на которые засекли время.
На рисунке 21.3 показана взаимосвязь между реальными RTT, которые мы можем определить из вывода команды tcpdump, и счетчиком тиков часов.