Организация связи между процессами с помощью установки логического соединения
Теперь посмотрим, какие действия нам понадобятся для организации взаимодействия процессов с помощью протокола TCP , то есть при помощи создания логического соединения. И начнем, как и в разделе "Использование модели клиент-сервер для взаимодействия удаленных процессов" текущего семинара, с простой жизненной аналогии. Если взаимодействие процессов через датаграммы напоминает общение людей по переписке, то для протокола TCP лучшей аналогией является общение людей по телефону.
Какие действия должен выполнить клиент для того, чтобы связаться по телефону с сервером? Во-первых, необходимо приобрести телефон (создать сокет), во-вторых, подключить его на АТС – получить номер (настроить адрес сокета). Далее требуется позвонить серверу (установить логическое соединение). После установления соединения можно неоднократно обмениваться с сервером информацией (писать и читать из потока данных). По окончании взаимодействия нужно повесить трубку (закрыть сокет).
Первые действия сервера аналогичны действиям клиента. Он должен приобрести телефон и подключить его на АТС (создать сокет и настроить его адрес). А вот дальше поведение клиента и сервера различно. Представьте себе, что телефоны изначально продаются с выключенным звонком. Звонить по ним можно, а вот принять звонок – нет. Для того чтобы вы могли пообщаться, необходимо включить звонок. В терминах сокетов это означает, что TCP-сокет по умолчанию создается в активном состоянии и предназначен не для приема, а для установления соединения. Для того чтобы соединение принять, сокет требуется перевести в пассивное состояние.
Если два человека беседуют по телефону, то попытка других людей дозвониться до них окажется неудачной. Будет идти сигнал "занято", и соединение не установится. В то же время хотелось бы, чтобы клиент в такой ситуации не получал отказ в обслуживании, а ожидал своей очереди. Подобное наблюдается в различных телефонных справочных, когда вы слышите "Ждите, пожалуйста, ответа. Вам обязательно ответит оператор".
Поэтому следующее действие сервера – это создание очереди для обслуживания клиентов. Далее сервер должен дождаться установления соединения, прочитать информацию, переданную по линии связи, обработать ее и отправить полученный результат обратно. Обмен информацией может осуществляться неоднократно. Заметим, что сокет, находящийся в пассивном состоянии, не предназначен для операций приема и передачи информации. Для общения на сервере во время установления соединения автоматически создается новый потоковый сокет, через который и производится обмен данными с клиентами. По окончании общения сервер "кладет трубку" (закрывает этот новый сокет) и отправляется ждать очередного звонка.
Схематично эти действия выглядят так, как показано на рисунке 15–16.7. Как и в случае протокола UDP отдельным действиям или их группам соответствуют системные вызовы, частично совпадающие с вызовами для протокола UDP. Их названия написаны справа от блоков соответствующих действий.
Для протокола TCP неравноправность процессов клиента и сервера видна особенно отчетливо в различии используемых системных вызовов. Для создания сокетов и там, и там по-прежнему используется системный вызов socket(). Затем наборы системных вызовов становятся различными.
Для привязки сервера к IP-адресу и номеру порта, как и в случае UDP- протокола, используется системный вызов bind(). Для процесса клиента эта привязка объединена с процессом установления соединения с сервером в новом системном вызове connect() и скрыта от глаз пользователя. Внутри этого вызова операционная система осуществляет настройку сокета на выбранный ею порт и на адрес любого сетевого интерфейса. Для перевода сокета на сервере в пассивное состояние и для создания очереди соединений служит системный вызов listen(). Сервер ожидает соединения и получает информацию об адресе соединившегося с ним клиента с помощью си стемного вызова accept(). Поскольку установленное логическое соединение выглядит со стороны процессов как канал связи, позволяющий обмениваться данными с помощью потоковой модели, для передачи и чтения информации оба системных вызова используют уже известные нам системные вызовы read() и write(), а для завершения соединения – системный вызов close().Необходимо отметить, что при работе с сокетами вызовы read() и write() обладают теми же особенностями поведения, что и при работе с pip’ами и FIFO (см. семинар 5).
Рис. 15-16.7. Схема взаимодействия клиента и сервера для протокола TCP