Шрифт:
Закладка:
6.4.1. Основы UDP
В наборе интернет-протоколов есть протокол передачи пользовательских дейтаграмм (User Datagram Protocol, UDP). Это транспортный протокол без установления соединения, описанный в RFC 768. UDP позволяет приложениям отправлять инкапсулированные IP-дейтаграммы без установления соединений.
С помощью UDP передаются сегменты, состоящие из 8-байтного заголовка, за которым следует поле Payload. Заголовок показан на илл. 6.27. Два порта служат для идентификации сокетов на отправляющем и принимающем устройствах. Когда приходит пакет UDP, содержимое Payload передается процессу, связанному с портом назначения. Это связывание происходит при выполнении BIND (или похожего примитива). Это показано на илл. 6.6 применительно к TCP (в UDP процесс связывания происходит так же). Представьте, что порты — это почтовые ящики, арендуемые приложениями, чтобы получать пакеты. Мы поговорим о них подробнее при обсуждении TCP, который тоже использует порты. В сущности, весь смысл применения UDP вместо обычного IP заключается как раз в указании портов отправителя и получателя. Без этих двух полей
Илл. 6.27. Заголовок UDP
на транспортном уровне невозможно было бы определить, что делать с входящим пакетом. В соответствии с полями портов вложенные сегменты доставляются соответствующим приложениям.
Информация о порте источника требуется прежде всего при создании ответа, пересылаемого отправителю. Копируя значения поля Source port (Порт отправителя) входящего сегмента в поле Destination port (Порт получателя) исходящего сегмента, процесс, отправляющий ответ, указывает, какому именно процессу на противоположной стороне этот ответ предназначен.
Поле UDP length состоит из заголовка и данных. Минимальная длина равна длине заголовка, то есть 8 байт. Максимальная длина составляет 65 515 байт — меньше, чем максимальное число, которое может поместиться в 16 бит, из-за ограничений на размер IP-пакета.
Необязательное поле Checksum служит для повышения надежности. Оно содержит контрольную сумму заголовка, данных и псевдозаголовка. При выполнении вычислений в поле Checksum устанавливается нулевое значение, а поле данных дополняется нулевым байтом, если его длина представляет собой нечетное число. Алгоритм вычисления контрольной суммы просто складывает все 16-разрядные слова в дополнительном коде, а затем вычисляет дополнение для всей суммы. В результате, когда получатель считает контрольную сумму всего сегмента, включая поле Checksum, результат должен быть равен 0. Если она не подсчитывается, ее значение равно 0 (настоящая нулевая контрольная сумма кодируется всеми единицами). Однако отключать функцию подсчета контрольной суммы глупо, за исключением одного случая — когда нужна высокая производительность (например, при передаче оцифрованной речи).
В случае IPv4 псевдозаголовок будет выглядеть так, как показано на илл. 6.28. Он содержит 32-разрядные IP-адреса отправителя и получателя, номер протокола для UDP (17) и счетчик байтов для UDP-сегмента (включая заголовок). В случае IPv6 он выглядит аналогично, хотя и с некоторыми отличиями. Включение псевдозаголовка в контрольную сумму UDP помогает обнаружить неверно доставленные пакеты, хотя это нарушает иерархию протоколов, так как IP-адреса в нем принадлежат IP-уровню, а не UDP-уровню. В TCP для контрольной суммы используется такой же псевдозаголовок.
Илл. 6.28. Псевдозаголовок, включаемый в контрольную сумму UDP
Наверное, стоит прямо сказать, чего UDP не делает. Итак, UDP не занимается управлением потоком данных, контролем перегрузки, повторной передачей после приема испорченного сегмента. Все это перекладывается на пользовательские процессы. Что же он делает? UDP предоставляет интерфейс для IP путем демультиплексирования нескольких процессов с использованием портов и необязательного сквозного обнаружения ошибок. На этом все.
Для процессов, которые должны управлять потоком, контролировать ошибки и временные интервалы, протокол UDP — это как раз то, что доктор прописал. Особенно это полезно в клиент-серверных ситуациях. Зачастую клиент отправляет короткий запрос серверу и надеется получить короткий ответ. Если запрос или ответ теряется, клиент по прошествии определенного временного интервала может попытаться еще раз. Это позволяет не только упростить код, но и уменьшить требуемое количество сообщений по сравнению с протоколами, которым требуется начальная настройка (например, TCP).
Служба имен доменов (Domain Name System, DNS) — это приложение, которое использует UDP именно так, как описано выше. Мы изучим его в главе 7. В двух словах, если программе нужно найти IP-адрес по имени хоста, например www.cs.berkeley.edu, она может отослать UDP-пакет с этим именем на сервер DNS. Сервер в ответ на запрос отправляет UDP-пакет с IP-адресом хоста. Не требуется никакой предварительной настройки, как и разрыва соединения после завершения задачи. По сети просто передаются два сообщения.
6.4.2. Вызов удаленной процедуры
В определенном смысле процессы отправки сообщения на удаленный хост и получения ответа очень похожи на вызов функции в языке программирования. В обоих случаях вы передаете один или несколько параметров и получаете результат. Это соображение навело разработчиков на мысль о том, что можно попробовать организовать запросно-ответное взаимодействие по сети, выполняемое в форме вызовов процедур. Такое решение позволяет упростить и сделать более привычной разработку сетевых приложений. Например, представьте себе процедуру запроса IP-адреса хоста, get_IP_address(host name), работающую посредством отправки UDP-пакетов на сервер DNS, ожидания ответа и отправки повторного запроса в случае наступления тайм-аута (если одна из сторон работает недостаточно быстро). Таким образом, все детали, связанные с особенностями сетевых технологий, скрыты от программиста.
Ключевая работа в этой области написана в 1984 году Бирреллом и Нельсоном (Birrell and Nelson). По сути, было предложено разрешить программам вызывать процедуры, расположенные на удаленных хостах. Когда процесс на устройстве 1 вызывает процедуру на устройстве 2, вызывающий процесс устройства 1 блокируется, и на устройстве 2 выполняется вызванная процедура. Информация от вызывающего процесса может передаваться в виде параметров и приходить обратно в виде результата процедуры. Передача сообщений по сети скрыта от программиста приложения. Такая технология известна под названием удаленного вызова процедур (Remote Procedure Call, RPC). Она легла в основу многих сетевых приложений. Традиционно вызывающая процедура считается клиентом, а вызываемая — сервером. Мы будем называть их так же.
Идея RPC состоит в том, чтобы сделать вызов удаленной процедуры максимально похожим на локальный вызов. В простейшем случае для вызова удаленной процедуры клиентская программа должна быть связана с маленькой библиотечной процедурой, клиентской заглушкой (client stub), которая представляет серверную процедуру в пространстве клиентских адресов. Аналогично сервер должен быть связан с процедурой под названием серверная заглушка (server stub). Эти процедуры скрывают тот факт, что вызов клиентом серверной процедуры не является локальным.
Реальные шаги, выполняемые при удаленном вызове процедуры, показаны на илл.