Шрифт:
Закладка:
Даже при известном значении SRTT выбор периода ожидания подтверждения — нетривиальная задача. В первых реализациях TCP это значение вычислялось как 2xRTT, но опыт показал, что постоянный множитель слишком негибкий и не реагирует на увеличение разброса. В частности, модели очередей случайного (то есть пуассоновского) трафика показывают, что когда нагрузка приближается к пропускной способности, задержка растет и становится крайне изменчивой. В результате может сработать таймер повторной передачи, после чего будет отправлена копия пакета, хотя оригинальный пакет все еще будет находиться в сети. Как правило, такие ситуации возникают именно при высокой нагрузке — и это не самое лучшее время для отправки в сеть лишних пакетов.
Чтобы решить эту проблему, Джейкобсон предложил сделать интервал времени ожидания чувствительным к отклонению RTT и к усредненному RTT. Для этого потребовалась еще одна сглаженная переменная — RTTVAR (RoundTrip Time Variation — изменение времени в пути туда-обратно), которая вычисляется по формуле:
Как и в предыдущем случае, это взвешенное скользящее среднее. Обычно β = 3/4. Значение интервала ожидания повторной передачи (RTO) устанавливается по формуле:
Множитель 4 выбран произвольно, однако умножение целого числа на 4 может быть выполнено одной командой сдвига, при этом менее 1 % всех пакетов придет с опозданием, превышающим четыре среднеквадратичных отклонения. Обратите внимание, что RTTVAR является не среднеквадратичным, а средним отклонением, но на практике это довольно близкие значения. В своей работе Джейкобсон приводит множество хитрых способов вычисления значений интервала ожидания с помощью только целочисленного сложения, вычитания и сдвига. Для современных хостов такая экономия не требуется, но она стала элементом культуры повсеместного применения TCP: он должен работать как на суперкомпьютерах, так и на небольших устройствах. Для RFID-чипов его пока еще не реализовали, но ведь все может быть.
Более подробные сведения о том, как вычислять этот интервал ожидания, а также начальные значения переменных, можно найти в RFC 2988. Для таймера повторной передачи минимальное значение также устанавливается равным 1 с, независимо от предварительной оценки. Это значение, выбранное с запасом (и в основном эмпирически), требуется, чтобы избежать выполнения лишних повторных передач на основании измерений (Оллман и Паксон; Allman and Paxson, 1999).
При сборе данных (R) для вычисления RTT возникает вопрос, что делать при повторной передаче сегмента. Когда для него приходит подтверждение, неясно, к какой передаче оно относится, первой или последней. Неверная догадка может серьезно нарушить работу RTO. Эта проблема была обнаружена радиолюбителем Филом Карном (Phil Karn). Его интересовал вопрос передачи TCP/IP-пакетов с помощью коротковолновой любительской радиосвязи, известной своей ненадежностью. Предложение Карна было очень простым: не обновлять оценки для сегментов, переданных повторно. Кроме того, при каждой повторной передаче время ожидания можно удваивать до тех пор, пока сегменты не пройдут с первой попытки. Это исправление получило название алгоритма Карна (Karn’s algorithm) (Карн и Партридж; Karn and Partridge, 1987) и применяется в большинстве реализаций TCP.
В протоколе TCP используется не только таймер повторной передачи, но и таймер настойчивости (persistence timer). Он предназначен для предотвращения следующей тупиковой ситуации. Получатель отправляет подтверждение, в котором указывает окно нулевого размера (это значит, что отправитель должен подождать). Через некоторое время получатель передает пакет с новым размером окна, но этот пакет теряется. Теперь обе стороны ожидают действий друг от друга. Когда срабатывает таймер настойчивости, отправитель высылает получателю пакет с вопросом, не изменилось ли текущее состояние. В ответ получатель сообщает текущий размер окна. Если он все еще равен нулю, таймер настойчивости запускается снова, и весь цикл повторяется. В случае увеличения окна отправитель может передавать данные.
В некоторых реализациях протокола используется третий таймер, называемый таймером проверки активности (keepalive timer). Он срабатывает, если соединение простаивает в течение долгого времени, заставляя одну сторону проверить, активна ли другая сторона. Если проверяющая сторона не получает ответа, соединение разрывается. Это свойство протокола довольно противоречиво, поскольку привносит дополнительные накладные расходы и может разорвать вполне жизнеспособное соединение из-за кратковременной потери связи.
И наконец, в каждом TCP-соединении используется таймер, запускаемый в состоянии TIME WAIT при закрытии соединения. Он отсчитывает двойное время жизни пакета, чтобы гарантировать, что после закрытия соединения созданные им пакеты исчезли.
6.5.10. Контроль перегрузки в TCP
Напоследок мы оставили одну из ключевых функций TCP: контроль перегрузки. Когда в сеть (в том числе в интернет) поступает больше данных, чем она способна обработать, возникают перегрузки. Если сетевой уровень узнает, что на маршрутизаторах скопились длинные очереди, он пытается справиться с этой ситуацией (пусть даже простым удалением пакетов). Транспортный уровень получает обратную связь от сетевого уровня, что позволяет ему следить за перегрузкой и при необходимости снижать скорость отправки. В интернете протокол TCP так же незаменим при контроле перегрузки, как и при транспортировке данных. Именно это делает его особенным.
Общие вопросы контроля перегрузки мы обсуждали в разделе 6.3. Основная мысль заключается в следующем: транспортный протокол, использующий закон управления AIMD при получении двоичных сигналов сети о перегрузке, сходится к справедливому и эффективному распределению пропускной способности. Контроль перегрузки в TCP реализует этот подход с помощью окна, а в качестве сигнала используется потеря пакетов. TCP поддерживает окно перегрузки (congestion window), размер которого равен числу байтов, которое отправитель может передавать по сети в любой момент времени. Соответственно, скорость отправки равна размеру окна, деленному на RTT. Размер окна задается в соответствии с правилом AIMD.
Напомним, что окно перегрузки существует в дополнение к окну управления потоком, определяющему количество байтов, которое получатель может поместить в буфер. Они отслеживаются параллельно, и число байтов, которое отправитель может передать в сеть, равно размеру меньшего из этих окон. Таким образом, эффективное окно — наименьшее из подходящих отправителю и получателю. Здесь необходимо участие обеих сторон. TCP останавливает отправку данных, если одно из окон временно заполнено. Если получатель говорит: «Высылайте 64 Кбайт», но при этом источник знает, что отправка более 32 Кбайт засорит сеть, он все же передаст 32 Кбайт. Если же отправителю известно, что сеть способна пропустить и большее количество данных, например 128 Кбайт, он передаст столько, сколько просит получатель (то есть 64 Кбайт). Окно управления потоком было описано ранее, поэтому в дальнейшем мы будем говорить только об окне перегрузки.
Современная схема контроля перегрузки была реализована в TCP