Компьютерные сети. 6-е изд. - Эндрю Таненбаум
Шрифт:
Интервал:
Закладка:
Диаграмму проще понять, если сначала проследовать по пути клиента (сплошная жирная линия), а затем — по пути сервера (жирный пунктир). Когда приложение на устройстве клиента вызывает операцию CONNECT, локальная TCP-подсистема создает запись соединения, помечает его состояние как SYN SENT и отправляет SYN-сегмент. Обратите внимание, что несколько приложений одновременно могут открыть множество соединений, и состояние каждого из них хранится в записи соединения. Когда прибывает сегмент SYN + ACK, TCP-подсистема отправляет последний ACK-сегмент «тройного рукопожатия» и переключается в состояние ESTABLISHED. В этом состоянии можно пересылать и получать данные.
Когда у приложения заканчиваются данные для передачи, оно выполняет операцию CLOSE, заставляющую локальную TCP-подсистему отправить FIN-сегмент и ждать ответного ACK-сегмента (пунктирный прямоугольник с пометкой «активное разъединение»). Когда прибывает подтверждение, происходит переход в состояние FIN WAIT 2, и одно направление соединения закрывается. Когда приходит встречный FIN-сегмент, в ответ на него также высылается подтверждение, после чего закрывается второе направление. Теперь обе стороны соединения закрыты, но TCP-подсистема ожидает в течение удвоенного максимального времени жизни пакета. Таким образом гарантируется, что ни один пакет этого соединения больше не перемещается по сети, даже если подтверждение было потеряно. Когда период ожидания истекает, TCP-подсистема удаляет запись о соединении.
Илл. 6.39. Конечный автомат TCP-соединения. Жирная сплошная линия показывает нормальный путь клиента. Жирным пунктиром показан нормальный путь сервера. Тонкими линиями обозначены необычные события. Для каждого перехода через косую черту указано, какое событие его вызывает и к выполнению какого действия он приводит
Далее рассмотрим управление соединением с точки зрения сервера. Он выполняет LISTEN и переходит в режим ожидания запросов соединения. Когда приходит SYN-сегмент, в ответ на него высылается подтверждение, после чего сервер переходит в состояние SYN RCVD (запрос соединения получен). Когда в ответ на SYN-подтверждение от клиента приходит ACK-сегмент, процедура «тройного рукопожатия» завершается и сервер переходит в состояние ESTABLISHED. Теперь можно передавать данные.
По окончании передачи данных клиент выполняет операцию CLOSE, в результате чего на сервер приходит FIN-сегмент (пунктирный прямоугольник, обозначенный как пассивное разъединение). Получив оповещение, сервер тоже выполняет CLOSE, и клиенту отправляется FIN-сегмент. Когда от клиента прибывает подтверждение, сервер разрывает соединение и удаляет запись о нем.
6.5.8. Раздвижное окно TCP
Как уже было сказано выше, управление окном в TCP решает проблемы подтверждения корректной доставки сегментов и выделения буферов получателя. Предположим, у получателя есть 4096-байтный буфер (илл. 6.40). Если отправитель передает 2048-байтный сегмент, который успешно принимается получателем, то последний подтверждает его получение. Однако при этом у получателя остается всего лишь 2048 байт свободного буферного пространства (пока приложение не заберет какое-то количество данных из буфера), о чем он и сообщает отправителю, указывая соответствующий размер окна (2048) и номер следующего ожидаемого байта.
После этого отправитель отправляет еще 2048 байт, получение которых подтверждается, но размер окна объявляется равным нулю. Отправитель должен прекратить передачу до тех пор, пока получающий хост не освободит место в буфере и не увеличит размер окна.
При нулевом размере окна отправитель не может отправлять сегменты, за исключением двух случаев. Во-первых, разрешается передавать срочные данные, например, чтобы пользователь мог уничтожить процесс, выполняющийся на удаленном компьютере. Во-вторых, отправитель может отправить 1-байтный сегмент, прося получателя повторить информацию о размере окна и ожидаемом следующем байте. Такой пакет называется пробным сегментом (window probe). Стандарт TCP прямо предусматривает эту возможность для предотвращения тупиковых ситуаций в случае потери объявления о размере окна.
Отправители не обязаны передавать данные сразу же, как только они приходят от приложения. Также никто не требует от получателей отправлять подтверждения как можно скорее. Например, было бы абсолютно логично, если бы TCP-подсистема на илл. 6.40, получив от приложения первые 2 Кбайт данных и зная, что размер окна равен 4 Кбайт, сохранила бы полученные данные в буфере до тех пор, пока не придут еще 2 Кбайт, чтобы передать сегмент из 4 Кбайт. Такая свобода действий может улучшить производительность.
Рассмотрим соединение (к примеру, telnet или SSH) с удаленным терминалом, реагирующим на каждое нажатие клавиши. При наихудшем сценарии, когда символ прибывает к передающей TCP-подсистеме, она создает 21-байтный TCP-сегмент и передает его IP-уровню, который, в свою очередь, отправляет 41-байтную IP-дейтаграмму. На принимающей стороне TCP-подсистема немедленно отвечает 40-байтным подтверждением (20 байт TCP-заголовка и 20 байт IP-заголовка). Затем, когда удаленный терминал прочитает этот байт из буфера, TCP-подсистема отправит обновленную информацию о размере буфера, передвинув окно на 1 байт вправо. Размер этого пакета также составляет 40 байт. Наконец, когда удаленный терминал обработает этот символ, он отправит обратно эхо, включенное в 41-байтный пакет. Итого для каждого введенного с клавиатуры символа пересылается четыре пакета общим размером 162 байта. При дефиците пропускной способности линий этот метод работы нежелателен.
Илл. 6.40. Управление окном в TCP
Чтобы улучшить ситуацию, многие реализации TCP используют отложенные подтверждения (delayed acknowledgements). Идея в том, чтобы задерживать подтверждения и обновления размера окна на время до 500 мс в надежде получить дополнительные данные и отправить подтверждение вместе с ними. Если терминал успеет выдать эхо в течение 500 мс, удаленной стороне нужно будет выслать только один 41-байтный пакет, таким образом, нагрузка на сеть снизится вдвое.
Хотя отложенные подтверждения и снижают нагрузку на сеть, тем не менее отправитель, передающий множество маленьких пакетов (к примеру, 41-байтные пакеты с 1 байтом реальных данных), по-прежнему работает неэффективно. Метод, позволяющий повысить эффективность, известен как алгоритм Нейгла (Nagle’s algorithm) (Nagle, 1984). Предложение Нейгла звучит просто: если данные поступают отправителю маленькими порциями, он просто передает первый фрагмент, а остальные помещает в буфер, пока не получит подтверждение приема первого фрагмента. После этого можно переслать все накопленные в буфере данные в виде одного TCP-сегмента и снова начать буферизацию до получения подтверждения о доставке следующего сегмента. Таким образом, в каждый момент времени может передаваться только один небольшой пакет. Если за время прохождения пакета в обе стороны приложение отправляет много порций данных, алгоритм Нейгла объединяет несколько таких порций в один сегмент, и нагрузка на сеть существенно снижается. Кроме того, согласно этому алгоритму, новый пакет должен быть отправлен, если объем данных в буфере превышает максимальный размер сегмента.
Алгоритм Нейгла широко применяется различными реализациями TCP, однако бывают ситуации,