Главная страница Взаимодействие нетривиальных процессов 3 int 4 mainCint argc, char **argv) 6 CLIENT *cl; 7 square in in: 8 square out *outp: 9 if (argc != 3) 10 err quit( usage: client <hostname> <integer-value> ): 11 cl = Clnt create(argv[l], SQUARE PROG, SQUAREJERS. tcp ); 12 in.argl = atoi(argv[2]): 13 if ( (outp = squareproc l(&in. cD) == NULL) 14 err quit( *s . clnt sperror(c1, argv[l])): 15 printf( result: ld\n , outp->resl): 16 exit(O): 17 } Подключение заголовочного файла, создаваемого rpcgen 2 Мы под1Слючаем заголовочный файл square.h, создаваемый функцией rpcgen. Объявление дескриптора клиента 6 Мы объявляем дескриптор клиента (client handle) с именем с1. Дескрипторы клиентов выглядят как обычные указатели на тип FILE (поэтому слово CLIENT пишется заглавными буквами). Получение дескриптора клиента 11 Мы вызываем функцию с1 nt create, создающую клиент RPC: #include <rpc/rpc.h> CLIENT *clnt create(const char *host. unsigned long prognum. unsigned long versnum. const char *protocol): /* Возвращает ненулевой дескриптор клиента в случае успешного завершения. NULL - в случае ошибки */ Как и с обычными указателями на тип FILE, нам безразлично, на что указывает дескриптор 1слиента. Скорее всего, это некоторая информационная структура, хранящаяся в ядре. Функция clnt create создает такую структуру и возвращает нам указатель на нее, а мы передаем его библиотеке RFC времени выполнения каждый раз при удаленном вызове процедуры. Первым аргументом с1 nt create должно быть имя или IP-адрес узла, на котором выполняется сервер. Вторым аргументом будет имя программы, третьим - номер версии. Оба эти значения берутся из спецификации (square. х, листинг 16.1). Последний аргумент позволяет указать протокол, обычно TCP или UDP. Вызов удаленной процедуры и вывод результата 12-15 Мы вызываем процедуру, причем первый аргумент указывает на входную структуру (&i п), а второй содержит дескриптор клиента. В большинстве стандарт- ных функций ввода-вывода дескриптор файла является последним аргументом. Точно так же и в вызовах RPC дескриптор клиента передается последним. Возвращаемое значение представляет собой указатель на структуру, в которой хранится результат работы сервера. Место под входную структуру выделяет программист, а под возвращаемую - пакет RPC. В файле спецификации square, х мы назвали процедуру SQUAREPROC, но из 1сли-ента мы вызываем squareproc l. Существует соглащение о преобразовании имени из файла спецификации к нижнему регистру и добавлении номера версии через символ подчеркивания. Со стороны сервера от нас требуется только написать процедуру. Функция mai п автоматически создается программой rpcgen. Текст процедуры приведен в листинге 16.3. Листинг 16.3. Процедура сервера, вызываемая с помощью Sun RPC sunrpc/squarel/server.c 1 #include unpipc.h 2 #include square.h 3 square out * 4 squareproc l svc(square in *inp, struct svc req *rqstp) 6 static square out out; 7 out.resl = inp->argl * inp->argl: 8 return(&out); Аргументы процедуры 3-4 Прежде всего мы замечаем, что к имени процедуры добавился суффикс svc. Это дает возможность использовать два прототипа функций ANSI С в файле square. х, один из которых определяет функцию, вызываемую клиентом в листинге 16.2 (она принимает дескриптор клиента), а второй - реальную функцию сервера (которая принимает другие аргументы). При вызове процедуры сервера первый аргумент является указателем на входную структуру, а второй - на структуру, передаваемую библиотекой RPC времени выполнения, которая содержит информацию о данном вызове (в этом примере игнорируется для простоты). Выполнение и возврат 6-8 Программа считывает входной аргумент и возводит его в квадрат. Результат сохраняется в структуре, адрес которой возвращается процедурой сервера. Поскольку мы возвращаем адрес переменной, эта переменная не может быть автоматической. Мы объявляем ее как статическую (static). ПРИМЕЧАНИЕ Внимательные читатели заметят, что это лишает нашу функцию возможности использования в защищенном поточном программировании. Мы обсудим это в разделе 16.2, где приведем пример защищенной функции. Откомпилируем клиент в системе Solaris, а сервер - в BSD/OS, запустим сервер, а затем клиент: Solaris % client bsdi 11 result: 121 Solaris % client 209.75.135.35 22 result: 484 В первом случае мы указываем имя узла сервера, а во втором - его IP-адрес. Этим мы демонстрируем возможность использования как имен, так и IP-адресов для задания узла в функции с1 nt create, Теперь продемонстрируем некоторые ошибки, возникающие при работе с1 nt create, если, например, не существует узел или на нем не запущена программа-сервер: Solaris % client nosuchhost 11 nosuchhost: RPC: Unknown host возвращается библиотекой RPC времени выполнения clnt create error возвращается нашей функцией-оберткой Solaris % client local host 11 local host: RPC: Program not registered clnt create error Мы написали клиентскую и серверную части программы и продемонстрировали их использование вообще без явного сетевого программирования. Клиент просто вызывает две функции, а сервер вообще состоит из одной функции. Все тонкости использования XTI в Solaris, сокетов в BSD/OS и сетевого ввода-вывода обрабатываются библиотекой RPC времени выполнения. В этом и состоит предназначение RPC - предоставлять возможность создания распределенных приложений без знания сетевого программирования. Другая немаловажная деталь данного примера заключается в том, что в системах Sparc под Solaris и Intel х86 под управлением BSD/OS используется разный порядок байтов. В Sparc используется порядок big endian ( тупоконечный ), а в Intel - little endian ( остроконечный ) (что мы показали в разделе 3.4 [24]). Отличия в порядке байтов также обрабатываются библиотекой RPC времени выполнения автоматически с использованием стандарта XDR (внешнее представление данных), который мы обсудим в разделе 16.8. Создание программы-клиента и программы-сервера в данном случае требует больше операций, чем для любой другой программы этой книги. Выполняемый файл клиента получается следующим образом: Solaris % rpcgen -С square.х Solaris % cc -с client.с -о client.о Solaris % cc -с square clnt.c -о square clnt.o Solaris % cc -с square xdr.c -о square xdr.o Solaris % cc -0 client client.о square clnt.o square xdr.o libunpipc.a -Insl Проблема о порядке байтов в слове сродни проблеме лилипутов из Путешествий Гулливера Д. Свифта, которые никак не могли договориться, с какого конца начинать есть яйцо. Именно оттуда англоязычные программисты взяли термины little-endian (остроконечник) и big-endian (тупоко-нечник), подразумевая little-end-first и big-end-first (младший или старший байт идет первым). - Примеч. перев.
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |