Шрифт:
Многие существующие на компьютере серверные процессы используются редко. Поддерживать каждый из них в активном состоянии с постоянным TSAP-адресом слишком расточительно. Альтернативный вариант в упрощенном виде показан на илл. 6.9. Он называется протоколом начального соединения (initial connection protocol). Вместо того чтобы назначать всем возможным серверам хорошо известные TSAP-адреса, каждое устройство, желающее предоставлять службы удаленным пользователям, обзаводится специальным обрабатывающим сервером (process server), действующим как прокси (посредник) для менее активно используемых серверов. В UNIX-системах такой сервер называется inetd. Он прослушивает одновременно несколько портов, ожидая запроса на соединение. Потенциальные пользователи начинают с того, что отправляют запрос CONNECT, указывая TSAP-адрес нужной им службы. Если ни один сервер их не ждет, они получают соединение с обрабатывающим сервером (илл. 6.9 (а)).
Илл. 6.9. Пользовательский процесс хоста 1 устанавливает соединение с почтовым сервером хоста 2 через обрабатывающий сервер
Получив запрос, обрабатывающий сервер порождает подпроцесс запрошенного сервера, передавая ему существующее соединение с пользователем. Новый сервер выполняет нужную задачу, в то время как обрабатывающий сервер возвращается к ожиданию новых запросов (см. илл. 6.9 (б)). Этот метод работает только в тех случаях, когда серверы могут создаваться по требованию.
6.2.2. Установление соединения
Установление соединения звучит просто, но неожиданно оказывается весьма трудным делом. На первый взгляд достаточно, чтобы одна транспортная подсистема отправила адресату сегмент CONNECTION REQUEST и получила в ответ CONNECTION ACCEPTED. Неприятность заключается в том, что сеть может потерять, задержать, повредить или дублировать пакеты. Это может сильно осложнить ситуацию.
Проблема: задержка и дублирование пакетов
Представьте себе настолько перегруженную сеть, что подтверждения практически никогда не доходят вовремя, все пакеты опаздывают и пересылаются повторно по два-три или более раза. Предположим, что сеть основана на дейтаграммах и что каждый пакет следует по своему маршруту. Некоторые пакеты могут застрять в «пробке» и прийти с большим опозданием, когда отправитель уже решит, что они утеряны.
Самый кошмарный сценарий выглядит следующим образом. Пользователь устанавливает соединение с банком и отправляет сообщение с запросом о переводе крупной суммы на счет ненадежного человека. К несчастью, пакеты решают прогуляться по замысловатому маршруту, посетив самые отдаленные уголки сети. Тем временем отправитель вынужден выполнить повторную передачу. На этот раз пакеты идут по кратчайшему пути и доставляются быстро, в результате чего отправитель разрывает соединение.
Происходит очередная неудача: первая порция пакетов выходит из укрытия и добирается до адресата в нужном порядке, предлагая банку установить новое соединение и снова выполнить перевод. У банка нет способа определить, что это дубликаты. Он решает, что это вторая независимая транзакция, и еще раз переводит деньги.
Такой сценарий может показаться маловероятным или даже неправдоподобным, но идея в том, что протоколы всегда должны работать корректно. Самые распространенные случаи должны быть реализованы с максимальной эффективностью, требующейся для высокой производительности сети, однако протокол должен уметь справляться и с редкими сценариями. В противном случае сеть будет ненадежной и может неожиданно дать сбой, даже не сообщив об ошибке.
В оставшейся части этого раздела мы будем изучать проблему задержавшихся дубликатов, уделяя особое внимание алгоритмам, устанавливающим соединение надежным способом. Основная проблема заключается в том, что задержавшиеся дубликаты распознаются как новые пакеты. Избежать копирования и задержки пакетов мы не можем. Но если это происходит, копии должны отвергаться и не обрабатываться как новые.
Эту проблему можно попытаться решить несколькими способами, но на самом деле ни один из них не является оптимальным. Так, например, можно использовать одноразовые транспортные адреса. При таком подходе каждый раз, когда требуется транспортный адрес, генерируется новый адрес. Когда соединение разрывается, он уничтожается. В этом случае задержавшиеся дубликаты не смогут найти транспортный адрес и, следовательно, перестанут представлять угрозу. Однако при этом будет сложнее установить соединение с процессом.
Другой вариант — каждому соединению присваивается уникальный идентификатор (последовательный номер, возрастающий на единицу для каждого установленного соединения). Он выбирается инициатором соединения и помещается в каждый сегмент, включая тот, который содержит CONNECTION REQUEST. После разрыва соединения транспортная подсистема может обновить таблицу, в которой хранятся прежние соединения в виде пар (одноранговая транспортная подсистема, идентификатор). При каждом новом запросе таблица проверяется на наличие соответствующего идентификатора (он мог остаться там от разорванного ранее соединения).
К сожалению, у этой схемы есть существенный изъян: требуется, чтобы каждая транспортная подсистема (как отправитель, так и получатель) практически бесконечно хранила определенное количество информации об истории соединений. Иначе, если устройство выйдет из строя и потеряет данные, оно не сможет определить, какие соединения уже использовались, а какие нет.
Вместо этого можно применить другой подход, который существенно упростит задачу. Нужно разработать механизм, уничтожающий устаревшие заблудившиеся пакеты и не позволяющий им существовать в сети бесконечно долго. При таком ограничении проблема станет более управляемой.