Выбрать главу

6.5.6. Разрыв TCP-соединения

Хотя TCP-соединения полнодуплексные, чтобы понять, как происходит их разъединение, лучше считать их парами симплексных соединений. Каждое симплексное соединение разрывается независимо от своего напарника. Чтобы его разорвать, любая из сторон может отправить TCP-сегмент с установленным битом FIN; это означает, что у него больше нет данных для передачи. После подтверждения TCP-сегмента это направление закрывается. Тем не менее данные могут продолжать передаваться неопределенно долго в противоположную сторону. Соединение разрывается, когда закрываются оба направления. Обычно для разрыва требуются четыре TCP-сегмента: по одному с битом FIN и по одному с битом ACK в каждом направлении. Первый бит ACK и второй бит FIN могут также содержаться в одном TCP-сегменте, что уменьшит количество сегментов до трех.

Как при телефонном разговоре, когда оба участника могут одновременно попрощаться и повесить трубку, оба конца TCP-соединения могут отправить FIN-сегменты в одно и то же время. Они оба получают обычные подтверждения, и соединение закрывается. По сути, между одновременным и последовательным разъединением нет никакой разницы.

Чтобы избежать проблемы «двух армий» (см. раздел 6.2.3), используются таймеры. Если ответ на отправленный FIN-сегмент не приходит в течение двух максимальных интервалов времени жизни пакета, отправитель FIN-сегмента разрывает соединение. Другая сторона в конце концов заметит, что ей никто не отвечает, и также отсоединится. Эта схема несовершенна, однако, учитывая недостижимость идеала, приходится пользоваться тем, что есть. На практике проблемы возникают довольно редко.

6.5.7. Модель управления TCP-соединением

Этапы, необходимые для установления и разрыва соединения, могут быть представлены в виде модели конечного автомата, 11 состояний которого перечислены на илл. 6.38. В каждом из этих состояний разрешены определенные события, в ответ на которые могут осуществляться действия. При возникновении каких-либо других событий сообщается об ошибке.

Каждое соединение начинается в состоянии CLOSED (закрыто). Оно может покинуть это состояние, предпринимая либо активную (CONNECT), либо пассивную (LISTEN) попытку открыть соединение. Если другая сторона осуществляет противоположное действие, соединение устанавливается и переходит в состояние ESTABLISHED. Инициатором разрыва соединения может выступить любая сторона. По завершении этого процесса соединение возвращается в состояние CLOSED.

Состояние

Описание

CLOSED

Закрыто. Соединение не является активным и не находится в процессе установления

LISTEN

Ожидание. Сервер ожидает входящего запроса

SYN RCVD

Прибыл запрос соединения. Ожидание подтверждения

SYN SENT

Запрос соединения отправлен. Приложение начало открывать соединение

ESTABLISHED

Установлено. Нормальное состояние передачи данных

FIN WAIT 1

Приложение сообщило, что ему больше нечего передавать

FIN WAIT 2

Другая сторона согласна разорвать соединение

TIME WAIT

Ожидание, пока из сети не исчезнут все пакеты

CLOSING

Обе стороны попытались одновременно закрыть соединение

CLOSE WAIT

Другая сторона инициировала разъединение

LAST ACK

Ожидание, пока из сети не исчезнут все пакеты

Илл. 6.38. Состояния конечного автомата, управляющего TCP-соединением

Конечный автомат показан на илл. 6.39. Типичный случай клиента, активно соединяющегося с пассивным сервером, показан жирными линиями — сплошными для клиента и пунктирными для сервера. Тонкие линии обозначают необычные последовательности событий. Каждая линия на илл. 6.39 маркирована парой событие/действие. Событие может представлять собой либо обращение пользователя к системной процедуре (CONNECT, LISTEN, SEND или CLOSE), либо прибытие сегмента (SYN, FIN, ACK или RST), либо, в одном случае, окончание периода ожидания, равного двойному времени жизни пакетов. Действие может состоять в отправке управляющего сегмента (SYN, FIN или RST). Впрочем, может не предприниматься никакого действия, что обозначается прочерком. В скобках приводятся комментарии.

Диаграмму проще понять, если сначала проследовать по пути клиента (сплошная жирная линия), а затем — по пути сервера (жирный пунктир). Когда приложение на устройстве клиента вызывает операцию 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 байт, получение которых подтверждается, но размер окна объявляется равным нулю. Отправитель должен прекратить передачу до тех пор, пока получающий хост не освободит место в буфере и не увеличит размер окна.