1 #include "unpipc.h"
2 #include "square.h"
3 square_out *
4 squareproc_l_svc(square_in *inp, struct svc_req *rqstp)
5 {
6 static square_out out;
7 out.res1 = inp-arg1 * inp-arg1;
8 return(out);
9 }
3-4 Прежде всего мы замечаем, что к имени процедуры добавился суффикс _svc. Это дает возможность использовать два прототипа функций ANSI С в файле square.x, один из которых определяет функцию, вызываемую клиентом в листинге 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-адресов для задания узла в функции clnt_create.
Теперь продемонстрируем некоторые ошибки, возникающие при работе clnt_create, если, например, не существует узел или на нем не запущена программа-сервер:
solaris % client nosuchhost 11
nosuchhost: RPC: Unknown host
clnt_create error
solaris % client localhost 11
localhost: rpc: program not registered
clnt_create error
Мы написали клиентскую и серверную части программы и продемонстрировали их использование вообще без явного сетевого программирования. Клиент просто вызывает две функции, а сервер вообще состоит из одной функции. Все тонкости использования XTI в Solaris, сокетов в BSD/OS и сетевого ввода-вывода обрабатываются библиотекой RPC времени выполнения. В этом и состоит предназначение RPC — предоставлять возможность создания распределенных приложений без знания сетевого программирования.
Другая немаловажная деталь данного примера заключается в том, что в системах Sparc под Solaris и Intel x86 под управлением BSD/OS используется разный порядок байтов. В Sparc используется порядок big endian («тупоконечный»[2]), а в Intel — little endian («остроконечный») (что мы показали в разделе 3.4 [24]). Отличия в порядке байтов также обрабатываются библиотекой RPC времени выполнения автоматически с использованием стандарта XDR (внешнее представление данных), который мы обсудим в разделе 16.8.
Создание программы-клиента и программы-сервера в данном случае требует больше операций, чем для любой другой программы этой книги. Выполняемый файл клиента получается следующим образом:
solaris % rpcgen-Сsquare.x
solaris % cc-сclient.с-оclient.о
solaris % cc-сsquare_clnt.c-оsquare_clnt.o
solaris % cc-сsquare_xdr.с-оsquare_xdr.o
solaris % cc-оclient client.оsquare_clnt.o square_xdr.o libunpipc.a –lnsl
Параметр –С говорит rpcgen о необходимости создания прототипов функций ANSI С в заголовочном файле square.h. Программа rpcgen также создает заглушку клиента (client stub) в файле с именем square_clnt.с и файл с именем square_xdr.с, который осуществляет преобразование данных в соответствии со стандартом XDR. Наша библиотека (содержащая функции, используемые в этой книге) называется libunpipc.a, а параметр –lnsl подключает системную библиотеку сетевых функций в Solaris (включая библиотеки RPC и XDR времени выполнения).