XDR


9.1. ВВЕДЕНИЕ

Несмотря на то, что большинство графических станций исполь-
зует для внутреннего представления данных формат IEEE, ОС UNIX
в настоящее время настолько широко распространена, что пере-
сылка двоичных данных между системами часто превращается в
проблему.

Например, целое число на ЭВМ Cray состоит из 64 бит, в то
время как на станциях Sun или HP целое число занимает 32 бита.
Более того, даже при одинаковом двоичном представлении струк-
туры языка Си могут быть выравнены по-разному - в зависимости
от компилятора архитектуры системы.

Система XDR (eXternal Data Representation), предложенная
фирмой Sun одновременно с NFS, предлагает стандарт представле-
ния данных для обмена информацией между машинами разных типов.


9.2. ФУНКЦИОНАЛЬНЫЕ ВОЗМОЖНОСТИ

9.2.1. Предлагаемый сервис

XDR позволяет описывать и представлять данные способом, не-
зависящим от конкретной машины, и таким образом позволяет об-
мениваться ими.

Какие существуют альтернативы общему формату XDR ?

- можно передавать данные только в формате ASCII. Этот метод
громоздок, значительно увеличивает объем передаваемых дан-
ных и опасен тем, что при преобразовании данных можно по-
терять точность.
- преобразование от случая к случаю, по мере необходимости.
При этом необходимо иметь столько программ преобразования,
сколько существует форматов представления данных. На рабо-
чих станциях UNIX очень часто используется формат IEEE,
который, таким образом, может служить основой для универ-
сального представления.

XDR - это одновременно, язык описания данных и средство их
представления. XDR - не зависит не только от аппаратных струк-
тур, но и от языков программирования. Таким образом, данные,
сформированные программой, написанной на Фортране и выполнен-
ной на ЭВМ Cray, могут быть считаны и обработаны программой,
написанной на Си и выполненной на рабочей станции Sun и наобо-
рот.

9.2.2. Механизмы реализации

XDR использует следующие соглашения :
- единственный формат для представления целых чисел : 32 би-
та, причем бит с наибольшим весом является младшим (пос-
ледним по номеру) - Рис. 9.1. ;
- кодирование вещественных чисел в формате IEEE
+
---------T--------T--------T--------¬
¦ байт 0 ¦ байт 1 ¦ байт 2 ¦ байт 3 ¦
L--------+--------+--------+---------
32 бита
<----------------------------------->

Рис. 9.1. - Порядок байтов XDR.

Этот тип формата неудобен тем, что кодирование приходится
выполнять даже тогда, когда в этом нет необходимости,например,
если обе машины имеют одно и то же внутреннее представление
данных. Однако, время преобразования мало, особенно в сравне-
нии со временем, затрачиваемом на пересылку в сети. По крайней
мере, этот подход отличается систематичностью - кодирование
всегда выполняется в формат XDR, а декодирование - во внутрен-
нее представление.

Типы данных, определяемые XDR, всегда имеют длину кратную 4
байтам. XDR дополняет закодированное значение нулями, чтобы
обеспечить кратность четырем байтам.

Данные, закодированные в формат XDR, не являются типизован-
ными. Это значит, что как передающее, так и принимающее уст-
ройство должны знать, данными какого типа они обмениваются.
Это, безусловно, ограничение, однако, оно снимает необходи-
мость дополнительно кодировать тип данных.


9.3. ЭКСПЛУАТАЦИЯ

9.3.1. Кодирование и декодирование

Передающий процесс кодирует данные с помощью примитивов биб-
лиотеки XDR. Принимающий процесс декодирует данные. Можно ис-
пользовать XDR в памяти, для файлов или в сети (Рис. 9.2.).

+
---------------¬ ---------------¬
¦ ПРОЦЕСС ¦ ¦ ПРОЦЕСС ¦
¦ ПЕРЕДАТЧИК ¦ ¦ ПРИЕМНИК ¦
L------T-------- L---------------
¦ ^
¦ ¦
КОДИРОВАНИЕ ДЕКОДИРОВАНИЕ
V ¦
---------------¬ -------+-------¬
¦ КОДИРОВАННЫЙ ¦ ¦ ПОТОК XDR ДЛЯ¦
¦ ПОТОК XDR ¦ ¦ДЕКОДИРОВАНИЯ ¦
L------T-------- L------T--------
¦ ¦
L---------------------
Память или файл или сеть

Рис. 9.2. Общий принцип эксплуатации XDR.

Библиотека XDR - это набор функций, написанных на языке Си,
предназначенных для преобразования данных из локального предс-
тавления в представление XDR и обратно.


Понятия потока и фильтра XDR
----------------------------

Поток XDR - это последовательность байтов, содержащая дан-
ные, представленные в формате XDR. Данные процесса-передатчика
преобразуются функциями XDR в представление XDR и заносятся в
поток. И наоборот, данные, предназначенные для процесса-прием-
ника, считываются из потока и преобразуются в соответствии с
внутренним машинным представлением.
Фильтр XDR - это процедура, которая кодирует или декодирует
определенный тип данных (целый,с плавающей точкой, массивы
...). Можно комбинировать уже имеющиеся фильтры для создания
новых. Самый простой способ создать новый фильтр для сложного
типа данных - это использование компилятора RPCGEN. Мы еще
вернемся к нему.
Фильтры XDR считывают и записывают данные в потоки.

Примитивы 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
------------

Как мы уже говорили, фильтры 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);
}

-----------------------------------------------------------

9.3.2. Использование RPCGEN

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 <rpc/rpc.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;
}


9.4. ИТОГИ

XDR - это стандарт представления, который позволяет предста-
вить данные независимо от архитектуры машины.

Библиотека XDR - это совокупность функций языка Си, преобра-
зующих данные из внутреннего локального представления в предс-
тавление XDR и обратно. Использование XDR влечет за собой не-
обходимость кодирования в формат XDR на станции-передатчике
данных и декодирования на станции-приемнике.

Поток XDR - это последовательность байтов, где данные предс-
тавлены в формате XDR.
Фильтр XDR - это процедура, кодирующая или декодирующая оп-
ределенный тип данных (целые, с плавающей точкой, массивы
...). Уже существующие фильтры можно комбинировать с целью
создать новые. Самый простой способ создать новый фильтр для
сложных данных - это использование препроцессора RPCGEN.

Есть возможность комбинировать сокеты и XDR :
- поток памяти с сокетами UDP
- поток записей с сокетами TCP.


БИБЛИОГРАФИЯ

Система XDR описана в конструкторской документации вместе с
RPC фирмы Sun : [AT&T 90],[HP 89a],[IBM 90],[Sun 90].

Интересные дополнения можно найти в [RIFFLET 90], и особенно
в [CORBIN 90] - где содержатся полные примеры.

Обновлено: 12.03.2015