Протокол TCP вынужден работать в совершенно иных условиях. Функция распределения плотности вероятности для времени прибытия подтверждения на этом уровне выглядит значительно более полого (рис. 6.36, б ), а вариативность достаточно велика. Поэтому предсказать, сколько потребуется времени для прохождения данных от отправителя до получателя и обратно, весьма непросто. Если выбрать величину интервала ожидания слишком малой (например, Т1 на рис. 6.36, б), возникнут излишние повторные передачи, забивающие Интернет бесполезными пакетами. Если же установить значение этого интервала слишком большим (T2), то из-за увеличения времени ожидания в случае потери пакета пострадает производительность. Более того, среднее значение и величина дисперсии времени прибытия подтверждений может изменяться всего за несколько секунд при возникновении и устранении перегрузки.
Решение заключается в использовании динамического алгоритма, постоянно изменяющего величину периода ожидания, основываясь на измерениях производительности сети. Алгоритм, применяемый в TCP, разработан Джекобсоном (Jacobson) в 1988 году и работает следующим образом. Для каждого соединения в протоколе TCP предусмотрена переменная SRTT (Smoothed Round-Trip Time — усредненная круговая задержка), в которой хранится текущее наилучшее ожидаемое время получения подтверждения для данного соединения. При передаче сегмента запускается таймер, который замеряет время, требуемое для получения подтверждения, а также запускает повторную передачу, если подтверждение не приходит в срок. Если подтверждение успевает вернуться, прежде чем истечет период ожидания, TCP-подсистема замеряет
время, потребовавшееся для его получения (R). Затем значение переменной SRTT обновляется по следующей формуле:
где а — весовой коэффициент, определяющий, насколько быстро забываются старые значения. Обычно он равен 7/8. Эта формула — взвешенное скользящее среднее (EWMA, Exponentially Weighed Moving Average) или фильтр низких частот, с помощью которого можно удалять шум.
Даже при известном значении переменной SRTT выбор периода ожидания подтверждения оказывается задачей нетривиальной. В первых реализациях TCP это значение вычислялось как 2xRTT, однако экспериментально было показано, что постоянный множитель является слишком негибким и плохо учитывает ситуации, при которых разброс значений времени прибытия подтверждения увеличивается. В частности, модели очередей случайного (то есть пуассоновского) трафика показывают, что когда нагрузка приближается к пропускной способности, задержка и ее разброс увеличиваются. В результате может сработать таймер повторной передачи, после чего будет отправлена копия пакета, хотя оригинальный пакет все еще будет в сети. Как правило, такие ситуации возникают именно при высокой нагрузке — и это не самое лучшее время для того, чтобы отправлять в сеть лишние пакеты.
Чтобы решить эту проблему, Джекобсон предложил сделать интервал времени ожидания чувствительным к отклонению круговой задержки и к усредненной круговой задержке. Для этого потребовалась еще одна сглаженная переменная RTTVAR (Round-Trip Time Variation, отклонение круговой задержки), которая вычисляется по формуле:
Как и в предыдущем случае, это взвешенное скользящее среднее. Как правило, в = 3/4. Значение интервала ожидания, RTO, устанавливается по формуле:
Выбор множителя 4 является произвольным, однако умножение целого числа на 4 может быть выполнено одной командой сдвига, а относительное количество излишних повторных передач при таком значении времени ожидания не превысит одного процента. Обратите внимание на то, что RTTVAR является не стандартным, а средним отклонением, но на практике их значения оказываются достаточно близкими. В работе Джекобсона приводится множество способов вычисления значений интервала ожидания с помощью только целочисленного сложения, вычитания и сдвига. Хотя для современных хостов такая экономия не представляет интереса, ее использования требует идея повсеместной применимости TCP: он должен работать как на суперкомпьютерах, так и на небольших устройствах. Для RFID-чипов его пока еще никто не реализовал, но вдруг? Кто знает, может быть.
Более подробные сведения о том, как вычислять это значение интервала ожидания, а также начальные значения переменных, можно найти в RFC 2988. Для таймера повторной передачи минимальное значение также устанавливается равным 1 с, независимо от предварительной оценки. Это значение нужно для того, чтобы избежать лишних повторных передач, основанных на измерениях (Allman и Paxson, 1999).
При получении данных для вычисления круговой задержки, R, возникает вопрос, что делать при повторной передаче сегмента. Когда наконец прибывает подтверждение для такого сегмента, непонятно, относится это подтверждение к первой передаче пакета или же к последней. Неверная догадка может серьезно снизить значение интервала ожидания. Эта проблема была обнаружена радиолюбителем Филом Карном (Phil Kam). Его интересовал вопрос передачи TCP/IP-пакетов с помощью коротковолновой любительской радиосвязи, известной своей ненадежностью. Предложение Ф. Карна было очень простым: не обновлять оценки для переданных повторно пакетов. Кроме того, при каждой повторной передаче время ожидания можно удваивать до тех пор, пока сегменты не пройдут с первой попытки. Это исправление получило название алгоритма Карна (Karn’s algorithm) (Kam и Partridge, 1987). Он применяется в большинстве реализаций протокола TCP.
В протоколе TCP используется не только таймер повторной передачи. Еще один применяемый в этом протоколе таймер называется таймером настойчивости (persistence timer). Он предназначен для предотвращения следующей тупиковой ситуации. Получатель посылает подтверждение, в котором указывает окно нулевого размера, давая тем самым отправителю команду подождать. Через некоторое время получатель посылает пакет с новым размером окна, но этот пакет теряется. Теперь обе стороны ожидают действий противоположной стороны. Когда срабатывает таймер настойчивости, отправитель посылает получателю пакет с вопросом, не изменилось ли текущее состояние. В ответ получатель сообщает текущий размер окна. Если он все еще равен нулю, таймер настойчивости запускается снова, и весь цикл повторяется. Если же окно увеличилось, отправитель может передавать данные.
В некоторых реализациях протокола используется третий таймер, называемый таймером проверки активности (keepalive timer). Когда соединение не используется в течение долгого времени, срабатывает таймер проверки активности, заставляя одну сторону проверить, есть ли еще кто живой на том конце соединения. Если проверяющая сторона не получает ответа, соединение разрывается. Эта особенность протокола довольно противоречива, поскольку она приносит дополнительные накладные расходы и может разорвать вполне жизнеспособное соединение из-за кратковременной потери связи.
Последний таймер, используемый в каждом TCP-соединении, — это таймер, запускаемый в состоянии TIME WAIT конечного автомата при закрытии соединения. Он отсчитывает двойное время жизни пакета, чтобы гарантировать, что после закрытия соединения в сети не останутся созданные им пакеты.
6.5.10. Контроль перегрузки в TCP
Мы оставили себе на закуску одну из ключевых функций TCP: контроль перегрузки. Когда в какую-либо сеть поступает больше данных, чем она способна обработать, в сети образуются заторы. Интернет в этом смысле не является исключением. Когда сетевой уровень узнает о том, что на маршрутизаторах скопились длинные очереди, он пытается справиться с этой ситуацией, пусть даже простым удалением пакетов. В задачи транспортного уровня входит обратная связь с сетевым уровнем, что позволяет ему следить за перегрузкой и при необходимости снижать скорость отправки пакетов.