//sunrpc/xdr1/opt2.h
7 struct mylist {
8 char *name;
9 long value;
10 struct mylist *next;
11 };
12 typedef struct mylist mylist;
13 struct args {
14 mylist *list;
15 };
16 typedef struct args args;
В листинге 16.23 приведен текст программы, инициализирующей связный список с тремя парами имя-значение и кодирующей его с помощью библиотеки XDR.
Листинг 16.23. Инициализация, кодирование связного списка и вывод результата
1 //sunrpc/xdr1/opt2.c
2 #include "unpipc.h"
3 #include "opt2.h"
4 int
5 main(int argc, char **argv)
6 {
7 int i;
8 XDR xhandle;
9 long *lptr;
10 args out; /* структура, которую мы заполняем */
11 char *buff; /* результат кодирования */
12 mylist nameval[4]; /* до четырех элементов в списке */
13 size_t size;
14 out.list = &nameval[2]; /* [2] –> [1] –> [0] */
15 nameval[2].name = "name1";
16 nameval[2].value = 0x1111;
17 nameval[2].next = &nameval[1];
18 nameval[1].name = "namee2";
19 nameval[1].value = 0x2222;
20 nameval[1].next = &nameval[0];
21 nameval[0].name = "nameee3";
22 nameval[0].value = 0x3333;
23 nameval[0].next = NULL;
24 buff = Malloc(BUFFSIZE); /* адрес должен быть кратен 4 */
25 xdrmem_create(&xhandle, buff, BUFFSIZE, XDR_ENCODE);
26 if (xdr_args(&xhandle, tout) != TRUE)
27 err_quit("xdr_args error");
28 size = xdr_getpos(&xhandle);
29 lptr = (long*)buff;
30 for (i = 0; i < size; i += 4)
31 printf("%8lxn", (long)ntohl(*lptr++));
32 exit(0);
33 }
Инициализация связного списка
11-22 Мы выделяем память под четыре элемента, но инициализируем только три из них. Первая запись nameval[2], потом nameval[1] и nameval[0]. Указатель на начало списка (out.list) устанавливается на &nameval[2]. Мы инициализируем список в таком порядке, чтобы показать, что библиотека XDR обрабатывает указатели и порядок в списке оказывается именно таким, каким он был в нашей программе, и не зависит от того, какие массивы для этого используются. Мы также инициализируем значения элементов списка шестнадцатеричными величинами, поскольку будем выводить их в этом формате.
Вывод программы показывает, что перед каждым элементом списка идет значение 1 в 4 байтах (что мы можем считать длиной массива переменной длины с одним элементом или булевским значением TRUE). Четвертая запись состоит из 4 байт, в которых записан 0. Она обозначает конец списка:
solaris % opt2
1 дальше идет один элемент
5 длина строки
6e616d65 имя(name)
31000000 1 и три байта дополнения
1111 значение
1 один элемент
6 длина строки
6e616d65 имя
65320000 е 2 и 2 байта дополнения
2222 значение
1 один элемент
7 длина строки
6e616d65 имя
65653300 е е 3 и 1 байт дополнения
3333 значение
0 конец списка
При декодировании списка библиотека XDR будет динамически выделять память под его элементы и указатели и связывать все это вместе, что позволит легко переходить от одного элемента списка к другому в программе на С.
16.9. Форматы пакетов RPC
На рис. 16.5 приведен формат запроса RPC в пакете TCP.
Поскольку TCP передает поток байтов и не предусматривает границ сообщений, приложение должно предусматривать способ разграничения сообщений. Sun RPC определяет запись как запрос или ответ, и каждая запись состоит из одного или более фрагментов. Каждый фрагмент начинается с 4-байтового значения: старший бит является флагом последнего фрагмента, а следующие 31 бит представляют собой счетчик (длина фрагмента). Если бит последнего фрагмента имеет значение 0, данный фрагмент не является последним в записи.
ПРИМЕЧАНИЕ
Это 4-байтовое значение передается в порядке big-endian, так же как и 4-байтовые целые в XDR, но оно не относится к стандарту XDR, поскольку он не предусматривает передачи битовых полей.
Если вместо TCP используется UDP, первое поле в заголовке UDP будет идентификатором транзакции (XID), как показано на рис. 16.7.
ПРИМЕЧАНИЕ
При использовании TCP на размер запроса и ответа RPC ограничения не накладываются, поскольку может использоваться любое количество фрагментов, а длина каждого из них задается 31-разрядным целым. Но при использовании протокола UDP запрос (и ответ) должен помещаться в дейтаграмму целиком, а максимальное количество данных в ней — 65507 байт (для IPv4). Во многих реализациях, предшествовавших TI-RPC, размер ограничивался значением около 8192 байт, поэтому если требуется передавать более 8000 байт, следует пользоваться протоколом TCP.
Рис. 16.5. Запрос RPC в пакете TCP
Приведем спецификацию XDR для запроса RPC, взятую из RFC 1831. Имена на рис. 16.5 взяты из этой спецификации:
enum autn_flavor {
AUTH_NONE = 0,
AUTH_SYS = 1,
AUTH_SHORT = 2
/* and more to be defined */
};
struct opaque_auth {
auth_flavor flavor;
opaque body<400>;
};
enum msg_type {
CALL = 0,
REPLY = 1
};
struct call_body {
unsigned int rpcvers; /* версия RPC: должна быть 2 */
unsigned int prog; /* номер программы */
unsigned int vers; /* номер версии */
unsigned int proc; /* номер процедуры */
opaque_auth cred; /* данные вызывающего */
opaque_auth verf; /* проверочная информация вызывающего */
/* параметры, относящиеся к процедуре */
};
struct rpc_msg {
unsigned int xid;
union switch (msg_type mtype) {
case CALL:
call_body cbody;
case REPLY:
reply_body rbody;
} body;
};
Содержимое скрытых данных переменной длины, содержащих сведения о пользователе и проверочную информацию, зависит от типа аутентификации. Для нулевой аутентификации, используемой по умолчанию, длина этих данных должна быть установлена в 0. Для аутентификации Unix эти данные содержат следующую структуру:
struct authsys_parms {
unsigned int stamp;
string machinename<255>;
unsigned int uid;
unsigned int gid;
unsigned int gids<16>;
};
Если тип аутентификации AUTH_SYS, тип проверки должен быть AUTH_NONE. Формат ответа RPC сложнее, чем формат запроса, поскольку в нем могут передаваться сообщения об ошибках. На рис. 16.6 показаны возможные варианты. На рис. 16.7 показан формат ответа RPC в случае успешного выполнения процедуры. Ответ передается по протоколу UDP.
Ниже приводится текст спецификации XDR ответа RPC, взятый из RFC 1831.
enum reply_stat {
MSG_ACCEPTED = 0,
MSG_DENIED = 1
};
enum accept_stat {
SUCCESS = 0, /* успешное завершение вызова RPC */
PROG_UNAVAIL = 1, /* требуемый номер программы недоступен */
PROG_MISMATCH = 2, /* требуемый номер версии недоступен */
PROC_UNAVAIL = 3, /* номер процедуры недоступен */
GARBAGE_ARGS = 4, /* не могу декодировать аргументы */
SYSTEM_ERR = 5 /* ошибка выделения памяти и т. п. */
};
struct accepted_reply {
opaque_auth verf;
union switch (accept_stat stat) {
case SUCCESS:
opaque results[0]; /* результаты, возвращаемые процедурой */
case PROG_MISMATCH:
struct {
unsigned int low; /* наименьший поддерживаемый номер программы */
unsigned int high; /* наибольший поддерживаемый номер программы */
} mismatch_info;
default: /* PROG_UNAVAIL, PROC_UNAVAIL, GARBAGE_ARGS, SYSTEM_ERR */
void;
} reply_data;
};
union reply_body switch (reply_stat stat) {
case MSG_ACCEPTED:
accepted_reply areply;
case MSG_DENIED:
rejected_reply rreply;
} reply;
Рис. 16.6. Возможные варианты ответов RPC
Вызов может быть отклонен сервером, если номер версии RPC не тот или возникает ошибка аутентификации:
enum reject_stat {
RPC_MISMATCH = 0, /* номер версии RPC отличен от 2 */
AUTH_ERROR =1 /* ошибка аутентификации */
};
enum auth_stat {
AUTH_OK = 0, /* успешное завершение */
/* ошибки на сервере */
AUTH_BADCRED = 1, /* ошибка в личных данных пользователя (нарушена контрольная сумма) */