Для того чтобы научиться использовать эти примитивы, рассмотрим систему, состоящую из сервера и нескольких удаленных клиентов. Вначале сервер выполняет примитив LISTEN — обычно для этого вызывается библиотечная процедура, которая обращается к системе. В результате сервер блокируется, пока клиент не обратится к нему. Когда клиент хочет связаться с сервером, он выполняет примитив CONNECT. Транспортная подсистема выполняет этот примитив, блокируя обратившегося к ней клиента и отсылая пакет серверу. Поле данных пакета содержит сообщение транспортного уровня, адресованное транспортной подсистеме сервера.
Примитив
Отправленный пакет
Значение
LISTEN (ОЖИДАТЬ)
(нет)
Блокировать сервер, пока какой-либо процесс не попытается соединиться
CONNECT (СОЕДИНИТЬ)
CONNECTION REQUEST (ЗАПРОС СОЕДИНЕНИЯ)
Активно пытаться установить соединение
SEND (ОТПРАВИТЬ)
ДАННЫЕ
Отправить информацию
RECEIVE (ПОЛУЧИТЬ)
(нет)
Блокировать сервер, пока не поступят данные
DISCONNECT (РАЗЪЕДИНИТЬ)
DISCONNECTION REQUEST (ЗАПРОС РАЗЪЕДИНЕНИЯ)
Прервать соединение
Илл. 6.2. Примитивы простой транспортной службы
Следует сказать пару слов о терминологии. За неимением лучшего термина, для сообщений, отправляемых одной транспортной подсистемой другой транспортной подсистеме, нам придется использовать понятие сегмент (segment). Оно используется в TCP, UDP и других интернет-протоколах. В более старых протоколах применялось громоздкое название модуль данных транспортного протокола (Transport Protocol Data Unit, TPDU). Сейчас оно практически не используется, однако вы можете встретить его в старых статьях и книгах.
Итак, сегменты, используемые транспортным уровнем, помещаются в пакеты, которыми обменивается сетевой уровень. Эти пакеты, в свою очередь, содержатся во фреймах, которые передает канальный уровень. Получив фрейм, процесс канального уровня обрабатывает его заголовок, и если адрес назначения совпадает с местом доставки, передает содержимое поля пользовательских данных наверх сетевой подсистеме. Cетевая подсистема похожим образом обрабатывает заголовок пакета и передает содержимое поля пользовательских данных пакета наверх транспортной подсистеме. Эта вложенность проиллюстрирована на илл. 6.3.
Илл. 6.3. Вложенность сегментов, пакетов и фреймов
Итак, вернемся к нашему примеру общения клиента и сервера. В результате запроса клиента CONNECT серверу отравляется сегмент, содержащий CONNECTION REQUEST (запрос соединения). Когда он прибывает, транспортная подсистема проверяет, заблокирован ли сервер примитивом LISTEN (то есть готов ли он к обработке запросов). Затем она снимает блокировку сервера и отсылает обратно клиенту сегмент CONNECTION ACCEPTED (соединение принято). Получив этот сегмент, клиент разблокируется, после чего соединение считается установленным.
Теперь клиент и сервер могут обмениваться данными с помощью примитивов SEND и RECEIVE. В простейшем случае каждая из сторон использует блокирующий примитив RECEIVE для перехода в режим ожидания сегмента, который передается другой стороной с помощью SEND. Когда сегмент прибывает, получатель разблокируется. Затем он может обработать полученный сегмент и отправить ответ. Такая схема прекрасно работает, пока обе стороны помнят, чья очередь передавать, а чья — принимать данные.
Обратите внимание, что на транспортном уровне даже простая однонаправленная пересылка данных сложнее, чем на сетевом. Каждый отправленный пакет будет в конце концов подтвержден. Пакеты с управляющими сегментами также подтверждаются, явно или неявно. Эти подтверждения управляются транспортными подсистемами при помощи протокола сетевого уровня и не видны пользователям транспортного уровня. Аналогично транспортные объекты занимаются проблемами таймеров и повторных передач. Все эти механизмы не видны пользователям транспортного уровня, для которых соединение представляется надежным битовым каналом. Биты поступают с одного конца канала и волшебным образом появляются на другом его конце в том же порядке. Эта способность скрывать сложность от пользователей свидетельствует о том, что многоуровневые протоколы являются довольно мощным инструментом.
Когда соединение больше не требуется, оно должно быть разорвано, чтобы освободить место в таблицах двух транспортных подсистем. Разъединение бывает симметричным и асимметричным. В асимметричном варианте одна из сторон может вызвать примитив DISCONNECT, в результате чего другая сторона получает управляющий сегмент DISCONNECTION REQUEST (запрос разъединения) и соединение разрывается.
В симметричном варианте каждое направление закрывается отдельно, независимо от другого. Когда одна сторона выполняет операцию DISCONNECT, это означает, что у нее больше нет данных для передачи, но при этом она все еще готова принимать данные от своего партнера. В этой схеме соединение разрывается, когда обе стороны выполняют операцию DISCONNECT.