Стивенс Уильям Ричард
Шрифт:
2 Мы подключаем заголовочный файл square.h, создаваемый функцией rpcgen.
6 Мы объявляем дескриптор клиента (client handle) с именем cl. Дескрипторы клиентов выглядят как обычные указатели на тип FILE (поэтому слово CLIENT пишется заглавными буквами).
11 Мы вызываем функцию clnt_create, создающую клиент RPC:
Как и с обычными указателями на тип FILE, нам безразлично, на что указывает дескриптор клиента. Скорее всего, это некоторая информационная структура, хранящаяся в ядре. Функция clnt_create создает такую структуру и возвращает нам указатель на нее, а мы передаем его библиотеке RPC времени выполнения каждый раз при удаленном вызове процедуры.
Первым аргументом clnt_create должно быть имя или IP-адрес узла, на котором выполняется сервер. Вторым аргументом будет имя программы, третьим — номер версии. Оба эти значения берутся из спецификации (square.х, листинг 16.1). Последний аргумент позволяет указать протокол, обычно TCP или UDP.
12-15 Мы вызываем процедуру, причем первый аргумент указывает на входную структуру (&in), а второй содержит дескриптор клиента. В большинстве стандартных функций ввода-вывода дескриптор файла является последним аргументом. Точно так же и в вызовах RPC дескриптор клиента передается последним. Возвращаемое значение представляет собой указатель на структуру, в которой хранится результат работы сервера. Место под входную структуру выделяет программист, а под возвращаемую — пакет RPC.
В файле спецификации square.x мы назвали процедуру SQUAREPROC, но из клиента мы вызываем squareproc_1. Существует соглашение о преобразовании имени из файла спецификации к нижнему регистру и добавлении номера версии через символ подчеркивания.
Со стороны сервера от нас требуется только написать процедуру. Функция main автоматически создается программой rpcgen. Текст процедуры приведен в листинге 16.3.
3-4 Прежде всего мы замечаем, что к имени процедуры добавился суффикс _svc. Это дает возможность использовать два прототипа функций ANSI С в файле square.x, один из которых определяет функцию, вызываемую клиентом в листинге 16.2 (она принимает дескриптор клиента), а второй — реальную функцию сервера (которая принимает другие аргументы).
При вызове процедуры сервера первый аргумент является указателем на входную структуру, а второй — на структуру, передаваемую библиотекой RPC времени выполнения, которая содержит информацию о данном вызове (в этом примере игнорируется для простоты).
6-8 Программа считывает входной аргумент и возводит его в квадрат. Результат сохраняется в структуре, адрес которой возвращается процедурой сервера. Поскольку мы возвращаем адрес переменной, эта переменная не может быть автоматической. Мы объявляем ее как статическую (static).
ПРИМЕЧАНИЕ
Внимательные читатели заметят, что это лишает нашу функцию возможности использования в защищенном поточном программировании. Мы обсудим это в разделе 16.2, где приведем пример защищенной функции.
Откомпилируем клиент в системе Solaris, а сервер — в BSD/OS, запустим сервер, а затем клиент:
В первом случае мы указываем имя узла сервера, а во втором — его IP-адрес. Этим мы демонстрируем возможность использования как имен, так и IP-адресов для задания узла в функции clnt_create.
Теперь продемонстрируем некоторые ошибки, возникающие при работе clnt_create, если, например, не существует узел или на нем не запущена программа-сервер: