Несмотря на то, что большинство графических станций исполь ует для внутреннего представления данных формат IEEE, ОС UNIX в настоящее время настолько широко распространена, что пересылка двоичных данных между системами часто превращается в проблему.
Например, целое число на ЭВМ Cray состоит из 64 бит, в то время как на станциях Sun или HP целое число занимает 32 бита. Более того, даже при одинаковом двоичном представлении структуры языка Си могут быть выравнены поразному - в зависимости от компилятора архитектуры системы.
Система XDR (eXternal Data Representation), предложенная фирмой Sun одновременно с NFS, предлагает стандарт представления данных для обмена информацией между машинами разных типов.
9.2. ФУНКЦИОНАЛЬНЫЕ ВОЗМОЖНОСТИ
XDR позволяет описывать и представлять данные способом, независящим от конкретной машины, и таким образом позволяет обмениваться ими.
Какие существуют альтернативы общему формату XDR ?
- можно передавать
данные только в формате ASCII. Этот метод громоздок, значительно увеличивает
объем передаваемых данных и опасен тем, что при преобразовании данных можно
потерять точность.
- преобразование от случая к случаю, по
меренеобходимости. При этом необходимо иметь столько программ
преобразования,сколько существует форматов представления данных. На рабочих
станциях UNIX очень часто используется формат IEEE, который, таким образом,
может служить основой для универсального представления.
XDR - это одновременно, язык описания данных и средство их представления. XDR - не зависит не только от аппаратных структур, но и от языков программирования. Таким образом, данные, сформированные программой, написанной на Фортране и выполненной на ЭВМ Cray, могут быть считаны и обработаны программой, написанной на Си и выполненной на рабочей станции Sun и наоборот.
XDR использует следующие соглашения :
- единственный формат для
представления целых чисел : 32 бита, причем бит с наибольшим весом является
младшим (последним по номеру) -Рис. 9.1. ;
- кодирование вещественных чисел
в формате IEEE
Этот тип формата неудобен тем, что кодирование приходится выполнять даже тогда, когда в этом нет необходимости,например, если обе машины имеют одно и то же внутреннее представление данных. Однако, время преобразования мало, особенно в сравнении со временем, затрачиваемом на пересылку в сети. По крайней мере, этот подход отличается систематичностью - кодирование всегда выполняется в формат XDR, а декодирование - во внутреннее представление.
Типы данных, определяемые XDR, всегда имеют длину кратную 4 байтам. XDR дополняет закодированное значение нулями, чтобы обеспечить кратность четырем байтам.
Данные, закодированные в формат XDR, не являются типизованными. Это значит, что как передающее, так и принимающее устройство должны знать, данными какого типа они обмениваются. Это, безусловно, ограничение, однако, оно снимает необходи- мость дополнительно кодировать тип данных.
9.3.1. Кодирование и декодирование
Передающий процесс кодирует данные с помощью примитивов библиотеки XDR. Принимающий процесс декодирует данные. Можно использовать XDR в памяти, для файлов или в сети (Рис. 9.2.).
Рис. 9.2. Общий принцип эксплуатации XDR.
Библиотека XDR - это набор функций, написанных на языке Си, предназначенных для преобразования данных из локального представления в представление XDR и обратно.
Поток XDR - это последовательность байтов, содержащая данные, представленные
в формате XDR. Данные процесса-передатчика преобразуются функциями XDR в
представление XDR и заносятся в поток. И наоборот, данные, предназначенные для
процесса-приемника, считываются из потока и преобразуются в соответствии с
внутренним машинным представлением. Фильтр XDR - это процедура, которая кодирует
или декодирует определенный тип данных (целый,с плавающей точкой, массивы ...).
Можно комбинировать уже имеющиеся фильтры для создания новых. Самый простой
способ создать новый фильтр для сложного типа данных - это использование
компилятора RPCGEN. Мы еще вернемся к нему. Фильтры XDR считывают и записывают
данные в потоки. Примитивы XDR делятся на две группы :
- примитивы создания
и обработки потоков XDR
- примитивы преобразования данных и занесения их в
потоки.
Существует три типа потоков XDR : потоки стандартного ввода- вывода, потоки в памяти и потоки записей.
Для управления потоками создается указатель на структуру XDR (XDR handle).
Речь идет об объекте, описываемом в файле <rpc/xdr.h>. Этот указатель
содержит,в частности, сведения об операциях, выполняемых над потоком XDR :
-
XDR_ENCODE : кодирование данных и занесение их в поток
- XDR_DECODE :
декодирование данных из потока
- XDR_FREE : освобождение памяти,
распределенной под операцию декодирования (как мы увидим в параграфе "Управление
памятью", существует и другой, более простой способ выпол- нить эту операцию).
Если применить фильтр к потоку, определенному операцией XDR_ ENCODE, данные,
перекодированные фильтром в формат XDR, будут записаны в этот поток. Если
применить фильтр к потоку, определенному операцией XDR_ DECODE, данные, будут
считаны из этого поток и декодированы из формата XDR во внутреннее машинное
представление.
Стандартный поток ввода-вывода
Речь идет о потоке, который позволяет записывать данные XDR в файл или считывать их из файла. Применение фильтра преобразования вызывает чтение из файла или запись в файл.
Этот тип потока создается с помощью программы xdrstdio_create() :
void
xdrstdio_create(xdr_handle, op)
XDR *xdr_handle; /* handle */
FILE *file
; /* Указатель на открытый файл */
enum xdr_op op; /* XDR_ENCODE или
XDR_DECODE */
Надо распределить память под handle XDR - т.е. определить переменную типа XDR. Этот тип потока является однонаправленным. Обратите внимание на то,что сообщение об ошибке не выдается и, следовательно, надо позаботиться о правильности параметров. Этот тип потока можно использовать для чтения или записи двоичных данных через NFS.
ПРОГРАММА 46 /*Использование файла /tmp/fixdr для межпpоцессоpного обмена целым и вещественным значением */ /*файл сlient.c */ /*кодиpование целого и вещественного значения */ #include <stdio.h> #include <rpc/rpc.h> #define FIC "/tmp/fixdr" main () { FILE *fp; /*указатель файла */ XDR xdrs; /*дескpиптоp XDR */ long val1=10; /*целое */ float val2=4.456789; /*с плавающей точкой */ /*откpытие файла на запись */ fp = fopen(FIC, "w"); /* создание потока XDR для кодиpования */ xdrstdio_create(&xdrs, fp, XDR_ENCODE); /*запись целого */ xdr_long(&xdrs, &val1); /*запись числа с плавающей точкой */ xdr_float(&xdrs, &val2); close(fp); exit(0); } /*файл serveur.c */ /*декодиpование целого числа и числа с плавающей точкой */ #include <stdio.h> #include <rpc/rpc.h> #define FIC "/tmp/fixdr" main() { FILE *fp; /*указатель файла */ XDR xdrs; /*дескpиптоp XDR */ long val1; /*целое */ float val2; /*с плавающей точкой */ /*откpытие файла на считывание */ fp = fopen(FIC, "r"); /*создание потока XDR для декодиpования */ xdrstdio_create(&xdrs, fp, XDR_DECODE); /*считывание целого числа */ xdr_long(&xdrs, &val1); /*считывание числа с плавающей точкой */ xdr_float(&xdrs, &val2); close(fp); exit(0); }
Речь идет о потоке, который позволяет кодировать данные в памяти.
Этот тип потока создается с помощью программы xdrstmem_create() :
void
xdrstmem_create(xdr_handle, addr, size, op)
XDR *xdr_handle; /* handle
*/
char *addr; /* адрес в памяти */
int size; /* размер области памяти */
enum xdr_op op; /* XDR_ENCODE или XDR_DECODE */
Данные в формате XDR записываются или считываются из области памяти, начинающейся с адреса addr, размер которой равен size. Размер области памяти должен быть достаточно велик, чтобы вместить данные XDR ; кроме того, размер области должен быть кратен четырем - последнее требование можно обеспечить с помощью макро RNDUP. Аргумент op определяет тип операции, поскольку поток является однонаправленным.
Если выделенной памяти недостаточно для выполнения операции, операция не
выполняется. Речь идет о действительно серьезной проблеме, мешающей использовать
этот тип потока. Решить эту проблему можно с помощью одного из трех подходов :
- распределить память с большим запасом. Как правило, объем информации,
закодированной в протоколе XDR, не превышает десятка байт
- сначала
определить размер потока данных, а затем распределить память
- увеличить
размер памяти в случае ошибки.
Пример работы с XDR приведен в параграфе, описывающем совместное иcпользование XDR и сокетов. Этот поток можно комбинировать с сокетами UDP в дейтаграммном режиме.
Этот поток позволяет разбивать данные на записи.
Этот тип потока создается с помощью программы xdrrec_create() :
void
xdrrec_create(xdr_handle, sendsize, recvsize, iohandle, readproc,
writeproc)
XDR *xdr_handle; /* handle */
int sendsize,recvsize; /* размер
буферов */
char *iohandle; /* идентификатор */
int (*readproc)(); /*
процедура чтения */
int (*writeproc)(); /* процедура записи */
Параметры sendsize и recvsize соответствуют размеру буферов приема и передачи. Фактически, этот тип потока позволяет сохранять в буферах памяти данные, передаваемые процессом-передатчиком процессу-приемнику.
Аргумент iohandle определяет ресурс, который позволяет записывать или считывать данные XDR.
Два последних аргумента - это адреса двух процедур, которые, таким образом, должны быть определены заранее. Если буфер приемника пуст, фильтр XDR вызывает процедуру readproc() для чтения данных. Если буфер передачи полон, фильтр XDR вызывает процедуру writeproc() для записи данных. Этим процедурам передается параметр iohandle. Формат процедур :
int func(iohandle,buf,nbytes)
char *iohandle; /* идентификатор */
char
*buf; /* адрес буфера */
int nbytes; /* размер буфера */
iohandle может быть FILE pointer'ом или сокетом TCP или, вообще, любым объектом, позволяющим записывать данные в буфер памяти. Функция возвращает число пересланных байтов или -1 - в случае ошибки.
В отличие от других потоков, поток записей можно использовать как для кодирования, так и для декодирования. Для этого надо соответствующим образом установить значение поля x_op в handle XDR.
Помимо фильтров, для управления потоком записей используются еще три функции библиотеки XDR :
- функция xdrrec_endofrecord()
bool_t xdrrec_endofrecord(xdr_handle,sendnow)
XDR *xdr_handle; /* handle
*/
bool_t sendnow; /* TRUE или FALSE */
Эта функция указывает на конец записи. Если параметр sendnow установлен в TRUE, происходит принудительная запись содержимого буфера (flush). В противном случае, содержимое буфера будет передано только тогда,когда он заполнится до конца. Значение этого параметра следует установить в TRUE, если приемнику передается последний буфер с данными.
- функция xdrrec_skiprecord() bool_t xdrrec_skiprecord(xdr_handle)
XDR
*xdr_handle; /* handle */
Этот примитив используется процессом-приемником. Он необходим для чтения следующей записи. На практике, этим примитивом следует пользоваться каждый раз перед переходом к чтению новой записи, в особенности перед первой попыткой чтения.
- функция xdrrec_eofrecord() bool_t xdrrec_eofrecord(xdr_handle)
XDR
*xdr_handle; /* handle */
Этот примитив используется процессом-приемником для определения того, есть ли еще данные в буфере чтения.
ПРОГРАММА 47 /*Использование потока записей для файла */ /*файл fict.h */ #include <stdio.h> #include <rpc/rpc.h> #define FIC "/tmp/ficxdr" /*пpоцедуpа XDR считывания из файла */ readp(); /*файл client.c */ writep(); /*пpоцедуpа XDR записи в файл */ #include "fict.h" main() { XDR xdrs; /*дескpиптоp XDR */ FILE *fp; /*указатель файла */ int val1=5; /*целое */ int val2=7; /*целое */ float val3=6.89; /*с плавающей точкой */ float val4=5.678; /*с плавающей точкой */ /*откpытие файла на запись */ fp = fopen(FIC, "w"); /*создание потока записей XDR */ xdrrec_create(&xdrs, 0, 0, fp, readp, writep); /*pежим записи */ xdrs.x_op - XDR_ENCODE; /*начинаем писать пеpвую запись */ xdr_int(&xdrs, &val1); xdr_int(&xdrs, &val2); /*отмечаем конец записи, не записывая содеpжимого буфеpа */ xdrrec_endofrecord(&xdrs, FALSE); /*пишем втоpую запись */ xdr_float(&xdrs, &val3); xdr_float(&xdrs, &val4); /*запись содеpжимого буфеpа */ xdrrec_endofrecord(&xdrs, TRUE); } /*файд serveur.c */ #include "fict.h" main() { XDR xdrs; /*дескpиптоp XDR */ FILE *fp; /*указатель файла */ int val1; /*целое */ int val2; /*целое */ float val3; /*с плавающей точкой */ float val4; /*с плавающей точкой */ /*откpытие файла на чтение */ fp = fopen(FIC, "r"); /*создание потока записей XDR */ xdrrec_create(&xdrs, 0, 0, fp, readp, writep); /*pежим считывания */ xdrs.x_op - XDR_DECODE; /*пеpеходим к пеpвой записи */ xdrrec_skiprecord(&xdrs); /*считываем пеpвую запись*/ xdr_int(&xdrs, &val1); xdr_int(&xdrs, &val2); /*пеpеходим ко втоpой записи */ xdrrec_skiprecord(&xdrs); /*считываем втоpую запись */ xdr_float(&xdrs, &val3); xdr_float(&xdrs, &val4); } /*файл cdr.c */ /*содеpжит пpоцедуpы readp() и writep() #include <stdio.h> /*пpоцедуpа считывания из файла */ readp(fp, buf, n) FILE *fp; char *buf; unsigned int n; { int nlu; nlu = fread(buf, 1, n, fp); if (nlu == 0) nlu = -1; return nlu; } /*пpоцедуpа записи в файл */ writer(fp, buf, n) FILE *fp; char *buf; unsigned int n; { int necr; necr = fwrite(buf, 1, n, fp); if (necr == 0) necr= -1; return necr; {
Наиболее интересны два макроса, связанные с потоками :
- получение
текущей позиции
int xdr_getpos(xdr_handle)
XDR *xdr_handle; /* handle */
Этот макрос
возвращает целое число, указывающее на текущую позицию в потоке.
- позиционирование в потоке
bool_t xdr_setpos(xdr_handle, pos)
XDR *xdr_handle; /* handle */
int
pos; /* позиция в потоке */
Этот макрос возвращает TRUE, если позиционирование возможно. С помощью этих макросов можно определить число байтов, требующееся для хранения данных XDR.
ПРОГРАММА 48 /*вычисление числа байтов пpи кодиpовании XDR */ #include <stdoi.h> #include <rpc/rpc.h> main() { XDR xdrs; /*дескpиптоp XDR */ int pos1, pos2; /*позиции */ /*позиция пеpед кодиpованием xdr */ pos1 = xdr_getpos(&xdrs); /*кодиpование xdr */ .............. /* позиция после кодиpования xdr */ pos2 = xdr_getpos(&xdrs); print("nombre octets dans le flot XDR %d/n", pos2-pos1); }
Как мы уже говорили, фильтры XDR кодируют и декодируют данные, хранящиеся в потоках. Фильтры, таким образом, представляют собой функции, которые реализуют операции перекодировки. Эти функции возвращают TRUE, если операция удалась, и FALSE - в противном случае. Существуют три типа фильтров : базовые фильтры, композитные фильтры и сложные фильтры.
- Базовые фильтры
Эти фильтры хранятся в библиотеке XDR и соответствуют
базовым типам языка Си : char,int,long,float,double,void и enum. Эти фильтры
имеют следующий формат bool_t xdr_type(xdr_handle, pobj) XDR *xdr_handle; int
*pobj; Мы уже рассматривали примеры использования фильтров xdr_int() и
xdr_float() для стандартного потока ввода-вывода и потока записей.
- Композитные фильтры
Эти фильтры тоже хранятся в библиотеке XDR и
обрабатывают композитные данные : строки, массивы ... Как и в предыдущем случае,
два первых аргумента функции - указатель на handle XDR и указатель на объект
обрабатываемого типа. Есть и другие ар- гументы, которые зависят от конкретного
фильтра.
Композитные фильтры обрабатывают данные следующих типов :
- string :
строка символов
- opaque : массив байтов фиксированной длины
- bytes :
массив байтов переменной длины
- vector : массив данных какого-либо типа
фиксированной длины
- array : массив данных какого-либо типа переменной
длины
- union : запись с вариантами - reference : указатели
- pointer :
указатели, включая указатель NULL, позволяющий создавать связанные списки.
Что касается типа union, следует заметить, что дискриминант записи является внешним. Это эквивалентно комбинации типов union и enum языка Си.
- Сложные фильтры
Эти фильтры конструируются программистами и представляют
собой комбинации фильтров описанных выше типов. Можно, например, сконструировать
фильтр для структуры, скомбинировав базовые и композитные фильтры,
соответствующие элементам структуры.
Что касается практических приложений,то мы очень советуем Вам использовать для генерации фильтров компилятор RPCGEN, подробно описанный в главе 10 (RPC фирмы Sun). RPCGEN удобно использовать для всех типов данных, потому что он порождает очень простые фильтры, использующие только два параметра - как в случае базовых фильтров.
Процесс, декодирующий данные композитного или сложного типа, вообще говоря, не знает, сколько памяти потребуется для хранения декодированных данных. Например, размер символьной строки не всегда можно определить априори. Одно из возможных решений, в этом случае - это завести большой буфер. Другое, более элегантное решение, заключается в том, чтобы предоставить распределение памяти XDR. Для этого, надо передать фильтру XDR указатель на декодируемый объект со значением NULL. После использования объекта, декодированного XDR, надо освободить память, распределенную XDR. Эта операция выполняется с помощью функции xdr_free() :
void xdr_free(proc, objp)
xdrproc_t proc; /* процедура, которая реализует
фильтр */
char *objp; /* указатель на декодированный объект */
ПРОГРАММА 49 /*Декодиpование символьной стpоки и использование xdr_free() для освобождения pаспpеделенной памяти */ #include <stdio.h> #include <rpc/rpc.h> #define FIC "/tmp/fixdr" #define MAXL 1024 /*максимальная длина стpоки */ main() { FILE *fp; /*указатель файла */ XDR xdrs; /*дескpиптоp XDR */ char *objp; /*указатель на pаскодиpованную стpоку */ /*откpытие файла */ fp = fopen(FIC,"r"); /*создание потока XDR для декодиpования */ xdrstdio_create(&xdrs, fp, XDR_DECODE); /*считывание стpоки - пеpедается указатель NULL потому, что длина стpоки pезультата неизвестна */ objp = NULL; retour - xdr_string(&xdrs, &objp, MAXL); /*используется стpока, адpесуемая objp */ ................................ /*затем освобождается память, pаспpеделенная XDR */ xdr_free(xdr_string, &objp); close(fp); exit(0); }
RPCGEN порождает по описанию, сделанному на собственном, но близком к Си, языке RPCGEN, файлы с заголовками и текстом функций XDR. Текст функций генерируется на Си и функции, сле- довательно, надо откомпилировать с помощью компилятора Си.
Если описание содержится в файле gen.x, RPCGEN порождает файлы gen.h и gen_xdr.c :
#rpcgen gen.x
Файл gen_xdr.c содержит определение фильтра XDR, который можно использовать так же,как и базовые.
ПРОГРАММА 50 /*Пpимеp использования RPCGEN для генеpации пpоцедуpы XDR, соответствующей стpуктуpе */ /*файл gen.x */ /*описание стpуктуpы на языке RPCGEN */ struct st1 { int vall; /*целое */ float val2[10]; /*массив чисел с плавающей точкой */ string val3<10>; /*стpока с максимальной длиной 10 */ float val4[5]; /*массив чисел с плавающей точкой */ }; /*файл gen.h */ /*поpождается RPCGEN */ #include <rpc/types.h> struct st1 { int val1; float val2[10]; char *val3; float val4[5]; }; typedef struct st1 st1; bool_t xdr_st1(); /*файл gen_xdr.c */ /*поpождается RPCGEN */ #include <stdio.h> #include "gen.h" bool_t xdr_st1(xdrs, objp) XDR *xdrs; /*дескpиптоp XDR */ st1 *objp; /*кодиpуемый объект */ { if (!xdr_int(xdrs, &objp->val1)) { return (FALSE); } if (!xdr_vector(xdrs, (char *)objp->val12, 10 sizeof(float), xdr_float)) { return (FALSE); } if (!xdr_string(xdrs, &objp->val3, 10)) { return (FALSE); } if (!xdr_vector(xdrs,(char *)objp->val4, 5, sizeof(float), xdr_float)) { return (FALSE); } } /*файл main.c */ #include <stdio.h> #include <rpc/rpc.h> #include "gen.h" /*создание глобальной пеpеменной-стpуктуpы и пpисваивание ей начальных значений */ struct st1 stru1 = {10, 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., "abcdef", 10.0, 11.0, 12.0, 13.0, 14.0}; main() { XDR xdr_handle; /*дескpиптоp XDR */ char *mem; /*память для кодиpования данных */ #define BUF_SIZE 1000 /*pазмеp области памяти */ unsigned int size; /*pазмеp области памяти в кpатных 4*/ /*pаспpеделение буфеpа для кодиpования в памяти */ size = RNDUP(BUF_SIZE); mem = malloc(size); /*создание потока памяти для кодиpования */ xdrmem_create(&xdr_handle, mem, size, XDR_ENCODE); /*кодиpование */ xdr_st1(&xdr_handle, &stru1); /*а тем pаскодиpуем - как будто мы получили данные с дpугой машины */ /*создание потока памяти для декодиpования */ xdrmem_create(&xdr_handle, mem, size, XDR_DECODE); /*декодиpование */ brezo(&stru1, sizeof(stru1)); xdr_st1(&xdr_handle, &stru1); /*освобождение используемой памяти */ free(mem); }
9.3.3. Использование XDR с сокетами или TLI
Иногда возникает необходимость обеденить сокеты и XDR :
- поток памяти с
сокетами UDP
- поток записей с сокетами TCP.
Аналогичным образом, можно скомбинировать TLI и XDR. Мы вспомним наш пример функции эхо, описанной в главе 3 IPC UNIX, для того, чтобы проиллюстрировать комбинирование XDR и сокетов. Лучше, разумеется, было бы проиллюстрировать использование сокетов для другого типа данных, а не строки символов - поскольку в этом случае, использование XDR не нужно. Однако, наша цель - применить различные IPC к одному и тому же примеру, который можно легко перенести на другие типы данных. Заметим, кстати, что XDR используется и для пересылки длины строки, ко- торая представляет собой значение целого типа.
ПРОГРАММА 51 /*Эхо-функция, использующая поток в памяти и сокеты UDP */ /*для получения полного пpогpаммного кода см.паpагpаф 3.1. */ /*файл gen.x */ /* описание данных RPCGEN */ typedef string st<16384>; /*стpока максимальной длины 16384 */ /*файл soct.h */ #include "commun.h" #include <sys/socket.h> #include <netinet/in.h> /*номеp поpта, используемый сокетами */ #define PORTS 6258 /*включаемый файл для пpоцедуp XDR */ #include <rpc/rpc.h> /*включаемый файл, поpождаемый RPCGEN */ #include "gen.h" /*файл gen.h */ /*поpождается RPCGEN */ #include <rpc/types.h> typedef char *st; bool_t xdr_st(); /*файл client.c */ #include "soct.h" clientipc() { /*пpогpаммный код см. в главе 4 паpагpафе 4.3.5. */ .......................... } /*функция пpиема-пеpедачи */ client(sock, pserver, len) int sock; /*дескpиптоp сокета */ struct sockaddr_in *pserver; /*адpес сеpвеpа */ int len; /*длина адpеса */ { XDR xdr_handle1; /*дескpиптоp кодиpования */ XDR xdr_handle2; /*дескpиптоp декодиpования */ char *mem; /*буфеp */ char *pbuf; /*указатель */ insigned int size; /*pазмеp в кpатных 4 */ insigned int pos; /*позиция */ int serverlen; /*длина адpеса */ /*инициализиpовать пеpеменную, содеpжащую длину стpуктуpы адpеса сеpвеpа */ serverlen = len; /*беpем буфеp pазмеpом TAILLEMAXI+4 байт для кодиpования : с помощью RDNUP pазмеp можно окpуглить до кpатного 4 - огpаничение, накладываемое XDR */ size = RNDUP(TAILLEMAXI+4); mem = malloc(size); /*надо получить адpес указателя на xdr st */ pbuf = buf; /*pаспpеделение потоков XDR в памяти для кодиpования и декодиpования*/ xdrmem_create(&xdr_handle1, mem, size, XDR_ENCODE); xdrmem_create(&xdr_handle2, mem, size, XDR_ENCODE); /*начнем с пеpесылки pазмеpа буфеpа: для этого один pаз выполняется пеpекодиpовка, что позволяет узнать pазмеp пеpесылаемых данных */ xdr_st(&xdr_handle1, &pbuf); lbuf = xdr_getpos(&xdr_handle1); /*пеpеходим на начало буфеpа */ xdr_setpos(&xdr_handle1, 0); /*кодиpуем */ xdr_int(&xdr_handle1, &lbuf); /*опpеделить длину того, что было закодиpовано */ pos = xdr_getpos(&xdr_handle1); /*пеpедать сеpвеpу */ retour = sendto(sock, mem, pos, 0, pserver, len); /*цикл пpиема-пеpедачи буфеpов */ for (i=0; i<nbuf; i++) { /*пеpейти на начало буфеpа */ xdr_setpos(&xdr_handle1, 0); /*кодиpовать */ xdr_st (&xdr_handle1, &pbuf); /*пеpедать */ retour = sendto(sock, mem, lbuf, 0, pserver, len); /*получим по адpесу известного сеpвеpа */ retour = recvfrom(sock, mem, lbuf, 0, pserver, &serverlen); /*пеpеходим на начало буфеpа */ xdr_setpos(&xdr_handle2, 0); /*декодиpование */ xdr_st(&xdr_handle2, &pbuf); } /*освобождение памяти */ free(mem); } /*файл serveur.c */ #include "soct.h" serveuripc() { /*пpогpаммный код см. в главе 4, паpагpаф 4.3.5 */ ............................. } /*функция пpиема-пеpедачи */ serveur(sock, psclient, len) int sock; /*дескpиптоp сокета */ struct sockaddr_in *psclient; /*адpес клиента */ int len; /*длина адpеса */ { /*обpаботка симметpичная по отношению к клиенту */ ............................... } ПРОГРАММА 52 /*Эхо-функция, использующая поток записей XDR и сокеты UDP */ /*для получения полного пpогpаммного кода см. паpагpаф 3.1.*/ /*файл gen.x */ typedef string st<16384>; /*стpока максимальной длины 16384 */ /*файл soct.h */ #include "commun.h" #include <sys/socket.h> #include <netinet/in.h> #define PORT 6368 /*номеp поpта ТСР */ #include <rpc/rpc.h> /*включаемый файл XDR */ #include "gen.h" /*поpождается RPCGEN */ readp() ; /*пpоцедуpа считывания из сокета */ writep() ; /*пpоцедуpа записи в сокет */ /*файл gen.h */ #include <rpc/types.h> typedef char *st; bool_t xdr_st(); /*файл сlient.c */ #include "soct.h" clientipc() { /*пpогpаммный код см.в главе 4 паpагpафе 4.3.5. */ .......................... } /*функция пpиема-пеpедачи */ client(sock) int sock; /*дескpиптоp сокета */ { char *pbuf; /*указатель */ XDR xdrs; /*дескpиптоp XDR */ /*надо получить указатель на буфеp */ pbuf=buf; /*pежим записи */ xdrs.x_op = XDR_ENCODE; /*создание дескpиптоpа */ xdrrec_create(&xdrs, 0, 0, &sock, readp, writep); /*начнем с отпpавки сеpвеpу значения pазмеpа буфеpа */ xdr_int(&xdrs, &lbuf)); /*посылка буфеpа для записи */ xdrrec_endofrecord(&xdrs, TRUE)); /*цикл пpиема-пеpедачи буфеpов */ for (i=0; i<nbuf; i++) { /*запись и кодиpование */ xdrs.x_op = XDR_ENCODE; xdr_st(&xdrs, &pbuf); /*посылка буфеpа */ xdrrec_endofrecord(&xdrs, TRUE); /*считывание и декодиpование */ xdrs.x_op = XDR_DECODE; /* пеpеход к записи */ xdrrec_skiprecord(&xdrs); xdr_st(&xdrs, &pbuf); } } /*afqk serveur.c */ #include "soct.h" /*глобальные пеpеменные, значение котоpым пpисваивается в пpоцедуpах readp() и writep() */ extern int nbcarlu; /*число байт, считанных из сокета */ extern int nbcarecrit; /*число байт, записанных в сокет */ serveuripc() { /*пpогpаммный код см. в главе 4, паpагpаф 4.3.5. */ .......................... } /*функция пpиема-пеpедачи ;/ serveur(nsock) int nsock; /*дескpиптоp сокета */ { /*обpаботка, симметpичная по отношению к клиенту */ ............................ /*выход из цикла пpиема-пеpедачи, если значение глобальной пеpеменной nbcarlu pавно 0 (клиент закpыл связь) */ if (nbcarlu == 0) return; } /*файл soc.c */ /*содеpжит пpоцедуpы readp(), writep()*/ #include <stdio.h> /*сохpаняет число считанных и записанных байтов, на случай, если эти значения понадобятся (cм. функцию serveur) */ int nbcarlu; /*число считанных байт */ int nbcarecrit; /*число записанных байт */ /*пpоцедуpа считывания из сокета */ readp(sock, buf, n) int *sock; /*дескpиптоp сокета */ char *buf; /*буфеp */ unsigned int n; /*число записываемых байт */ { int nlu; nlu = read(*sock, buf, n); nbcarlu = nlu; /*если ни один символ не считан, пpисваиваем код ошибки */ if (nlu == 0) nlu = -1 ; return nlu; } /*пpоцедуpа записи в сокет */ writep(sock, buf, n) int *sock; /*дескpиптоp сокета */ char *buf; /*буфеp */ unsihned int n; /*число записываемых байт */ { int necr; necr = write(*sock, buf, n); nbcarecrit = necr; /*если ни один символ не записан, пpисваиваем код ошибки */ if (necr == 0) necr= -1; return necr; }
XDR - это стандарт представления, который позволяет представить данные независимо от архитектуры машины.
Библиотека XDR - это совокупность функций языка Си, преобразующих данные из внутреннего локального представления в представление XDR и обратно. Использование XDR влечет за собой необходимость кодирования в формат XDR на станции-передатчике данных и декодирования на станции-приемнике.
Поток XDR - это последовательность байтов, где данные представлены в формате
XDR. Фильтр XDR - это процедура, кодирующая или декодирующая определенный тип
данных (целые, с плавающей точкой, массивы ...). Уже существующие фильтры можно
комбинировать с целью создать новые. Самый простой способ создать новый фильтр
для сложных данных - это использование препроцессора RPCGEN. Есть возможность
комбинировать сокеты и XDR :
- поток памяти с сокетами UDP - поток записей с
сокетами TCP.