Nets2010 (1131259), страница 49
Текст из файла (страница 49)
Как уже было сказано, установление ТСР-соединения происходит по протоколу трехкратного рукопожатия. Флаги SYN и ASK в заголовке сегмента используются для реализации примитивов CONNECTION REQUEST и CONNECTION ACCEPTED. Флаг RST используется для реализации примитива REJECT. Это означает, что указанные выше примитивы вызывают посылку ТСР-пакета с установленным соответствующим флагом.
Когда приходит запрос на соединение по определенному порту, транспортный агент проверяет, есть ли процесс, который выполнил примитив LISTEN на этом порту. Если такой процесс есть, то ему передается управление. Если такого процесса нет, то в ответ идет отказ от установления соединения.
Если два хоста одновременно пытаются установить соединение между двумя одинаковыми сокетами (коллизия), то поскольку каждое соединение идентифицируется парой сокетов, будет установлено только одно из соединений.
Таймер для последовательных номеров сегментов тактируется с частотой 4 мксек., максимальное время жизни пакета - 120 сек. Напомним, что начальный номер сегментов никогда не равен нулю, по соображениям, приведенным ранее. Для генерации последовательных номеров сегментов используют механизм логических часов.
ТСР-соединение, как уже говорилось, - дуплексное, т.е. в каждом направлении данные передаются независимо и соединение разрывается независимо по каждому направлению. Поэтому лучше всего представлять его как два симплексных соединения. Если в очередном сегменте флаг FIN=1, то в этом направлении данных больше не будет. При получении подтверждения для этого сегмента соединение в этом направлении считается разорванным. В другом направлении передача может продолжаться сколь угодно долго. Если подтверждения на первый FIN нет в течение двух интервалов жизни пакетов, то по time-out соединение считается разорванным. Противоположная сторона также по истечении этого периода времени узнает, что никто от нее не ждет ответа. В таблице 6-18 и на рисунке 6-19 представлена процедура установления и разрыва соединения в виде диаграммы конечного автомата.
Таблица 6-18. Состояния, используемые в конечном автомате управления TCP-соединениями
Состояние | Описание |
CLOSED | Нет активных или ожидающих соединений |
LISTEN | Сервер ожидает входящего вызова |
SYN RCVD | Запрос на соединение доставлен; ожидание подтверждения |
SYN SENT | Приложение открывает соединение |
ESTABLISHED | Состояние нормальной передачи данных |
FIN WAIT 1 | Приложение сообщило об окончании работы |
FIN WAIT 2 | Другая сторона согласилась разорвать соединение |
TIMED WAIT | Ожидание, пока все пакеты прекратят свое существование |
CLOSING | Попытка обоих сторон одновременно закрыть соединение |
CLOSE WAIT | Противоположная сторона инициировала разрыв |
LAST ACK | Ожидание, пока все пакеты прекратят свое существование |
Рисунок 6-19. Конечный автомат управления TCP-соединениями
Управление окнами в протоколе ТСР, как в управлении потоком на канальном уровне, не связано прямо с поступлением подтверждений. Предположим, что у получателя есть буферы в 4096 байт, как показано на рисунке 6-20. Если отправитель послал сегмент в 2048 байт, то получатель, получив и подтвердив этот сегмент, будет показывать окно в 2048 байт до тех пор, пока приложение не возьмет часть данных из полученного сегмента в 2048 байт. Отправитель посылает следующие 2048 байт. Теперь размер окна равен 0 байт.
Когда поле WIN=0, отправитель может послать сегмент в двух случаях. Первый - если это данные URGENT. Например, когда требуется убить процесс на удаленной машине. Второй - если это однобайтовый сегмент. Это может потребоваться, чтобы заставить получателя показать текущее состояние буфера, что очень важно, так как позволяет обойти тупик.
Заметим, что протокол ТСР не требует от агента-отправителя сразу передавать сегмент, как только данные поступили от приложения. Эту свободу ТСР использует, чтобы повысить свою производительность. Рассмотрим, к примеру, удаленный редактор TELNET. Если передавать по сети каждое движение пользователя мышкой или нажатие им клавиши на клавиатуре, то обмен будет очень не эффективным. На передачу одного символа будет приходиться передача сегмента в 160 байт. Поэтому часто при реализации протокола ТСР вводят специальную задержку на посылку подтверждения и состояния окна, чтобы дать отправителю накопить буфер для отправки. Другую стратегию предложил Нагл (Nagle) – если работа идет с приложением, которое генерирует однобайтные сообщения, то надо первый байт послать, а все остальные буферизовать до тех пор, пока не придет подтверждение на посланный байт. Все буферизованные байты нужно послать одним сегментом, после чего буферизовать все байты, пока не придет подтверждение на посланный сегмент.
Алгоритм Нагла работает хорошо. Однако есть приложения, где его следует отключить, – X-Windows. Здесь перемещения мыши по экрану надо пересылать сразу без буферизации.
Другая проблема, которая может существенно понизить производительность протокола ТСР – т.н. «синдром дурацкого окна» (рисунок 6-21). Он возникает, когда приложение на стороне отправителя передает ТСР-агенту данные большими блоками, а приложение на стороне получателя читает данные побайтно! В этой ситуации может произойти следующее. Буфер на стороне получателя полон и отправитель знает об этом. Приложение-получатель считывает один байт из буфера. ТСР-агент получателя радостно шлет сообщение о доступном буфере в один байт. Отправитель обязан послать один байт. После чего буфер получателя опять полон, и т.д.
Рисунок 6-21. «Синдром дурацкого окна»
Кларк предложил запретить сообщать получателю в таких случаях об освободившемся месте на один байт. Получателя принуждают ждать, пока либо не освободится размер, равный максимальной длине сегмента, объявленной при установлении соединения, либо не освободится половина буфера. Отправитель также должен стараться избегать крохотных сегментов, а посылать большие.
У ТСР-агента получателя также есть средства улучшить производительность соединения. Например, можно запретить приложению использовать примитив READ до тех пор, пока у агента есть данные в буфере. Конечно, такое решение увеличит время отклика, но для неинтерактивных приложений это не страшно.
Другая проблема для получателя, понижающая производительность соединения, - нарушение порядка поступления сегментов. Например, если поступили сегменты 0, 1, 2, 4, 5, 6, 7, получатель может подтвердить сегменты 0-2, забуферизовать сегменты 4-7. Тогда отправитель по time-out перешлет сегмент 3, после чего получатель подтвердит 4-7 сегменты.
Здесь мы рассмотрим, как протокол ТСР борется с перегрузками. В основе всех методов лежит принцип сохранения количества пакетов: не посылать новый, пока старый не покинет сеть, т.е. не будет доставлен. Основная идея очень проста - при возникновении перегрузки не посылать новых пакетов. В протоколе ТСР это реализуется динамически с помощью механизма окон.
Прежде всего, протокол ТСР обнаруживает перегрузку по росту числа time_out. Если эта величина превышает некоторый предел, являющийся параметром протокола, то это фиксируется как перегрузка. Причин может быть две – шум в канале и сброс пакетов маршрутизатором. Различить их сложно. В наши дни каналы достаточно надежные, так что актуальной остается вторая причина.
Перегрузки возникают по двум причинам: нехватка буфера на стороне получателя – недостаточная емкость получателя; перегрузка внутри сети – недостаточная емкость сети.
В Internet эти ситуации различаются как внутренняя емкость сети и емкость получателя. Поэтому каждый отправитель поддерживает два окна - обычное окно отправителя и окно перегрузки. Каждое показывает количество байтов, которое отправитель может послать. Фактически отправляемое количество байтов - минимум из этих двух величин.
Сначала окно перегрузки полагают равным размеру максимального сегмента для данного соединения. Если сегмент успешно (без time_out) был передан, то окно перегрузки увеличивается вдвое. Это увеличение будет происходить до тех пор, пока либо не наступит time_out и произойдет возврат к предыдущему значению, либо размер окна перегрузки не достигнет размера окна получателя. Этот алгоритм называется slow start - медленный старт.
Другой параметр управления перегрузками в Internet – порог (threshold). Алгоритм медленного старта при возникновении перегрузки устанавливает этот параметр равным половине длины окна перегрузки, а окно перегрузки - равным размеру максимального сегмента. Окно перегрузки растет экспоненциально до тех пор, пока не сравняется с порогом, после чего оно растет линейно, пока не достигнет размера окна получателя. На этом рост прекращается до первой перегрузки. Работа этого алгоритма показана на рисунке 6-23.
Рисунок 6-23. Алгоритм управления перегрузками в internet
Протокол ТСР использует несколько таймеров для управления передачей. Наиболее важный из них - таймер повторной передачи. Этот таймер устанавливают, когда отправляют сегмент. (Напомним, что так мы называем TPDU-пакет.) Если подтверждение пришло до исчерпания этого таймера, то его останавливают и сбрасывают. Если таймер исчерпан, то сегмент посылают повторно.
Здесь основная проблема - как удачно выбрать величину time_out: временной интервал, по истечении которого сегмент надо передать повторно. Как мы знаем, эта проблема встречается и на других уровнях. Однако на транспортном уровне она имеет особенность, которая заключается в следующем. На канальном уровне дисперсия величины задержки подтверждения имеет ярко выраженный максимум. Другими словами, ее разброс невелик. Величину time_out на этом уровне устанавливают чуть больше ожидаемой величины прихода подтверждения. На транспортном уровне функция распределения величины задержки подтверждения носит более гладкий характер, чем на канальном уровне. Поэтому предсказать величину времени, которая нужна для передачи данных от источника до получателя и передачи подтверждения от получателя до источника, очень трудно. Заведомо эта величина не должна быть постоянной в силу гладкости функции распределения.
В основе используемого в протоколе ТСР алгоритма, предложенного Якобсоном в 1988 году, лежит специальная переменная RTT для получения оптимального значения величины time_out (Round Trip Time), значение которой постоянно модифицируется. В этой переменной хранится наименьшее время подтверждения. При каждой передаче сегмента замеряется величина задержки подтверждения М. Если при очередной передаче подтверждение поступило прежде, чем наступил time_out, значение переменной RTT немного уменьшают, в противном случае - увеличивают по формуле:
RTT = λRTT + (1- λ)M, где λ=0,87.
Однако, даже зная величину RTT, определить величину ожидания оказалось непросто. Якобсон предложил вычислять величину D - отклонения между ожидаемой величиной задержки и измеренной по формуле:
D = λD + (1-λ) |RTT - M|.
Кроме этого, Якобсон показал как, зная D, вычислить величину time_out по формуле: