Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 [ 134 ] 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

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 (младший или старший байт идет первым). - Примеч. перев.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 [ 134 ] 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.