next up previous contents
Next: Директивы препроцессора Up: Удаленный вызов процедур Previous: Преобразование локальных процедур в   Contents

Передача сложных структур данных

rpcgen можно использовать для создания процедур XDR, которые будут преобразовывать локальные структуры данных в формат XDR и наоборот.

Пусть dir.x содержит сервис удаленного чтения каталога, созданный с помощью rpcgen, процедуры сервера и процедуры XDR.

Файл описания протокола RPC dir.x имеет следующий вид:

/*

* dir.x: Протокол вывода удаленного каталога

*/

 /*максимальная длина элемента каталога */

const MAXNAMELEN = 255;

/* элемент каталога */

typedef string nametype<MAXNAMELEN>; 

/* ссылка в списке */

typedef struct namenode *namelist; 

/* Узел в списке каталога */

struct namenode {

  nametype name; /* имя элемента каталога */

  namelist next; /* следующий элемент */

};

union readdir_res switch (int errno) {

       case 0:

            namelist list; /* нет ошибок:

                возвращает оглавление каталога */

       default:

            void; /*возникла ошибка: возвращать нечего*/

};

 

/* Определение программы каталога */

program DIRPROG {

    version DIRVERS {

        readdir_res

        READDIR(nametype) = 1;

    } = 1;

} = 0x20000076;

Имеется возможность переопределить типы (например, readdir_res в примере выше) с использованием ключевых слов языка RPC
struct, union, и enum. Эти ключевые слова не используются в последующих декларациях переменных таких типов. Например, если определяется объединение, my_un, то объявляется использование только my_un, а не union my_un. rpcgen собирает объединения RPC в структуры C. Не следует объявлять объединения C, используя ключевое слово union.

При запуске rpcgen для dir.x создаются четыре файла:

Последний файл содержит процедуры XDR для преобразования объявленных типов данных из представления конкретной аппаратной платформы в формат XDR, и наоборот. rpcgen предполагает, что libnsl содержит процедуру для каждого типа данных RPCL, используемого в файле .x. Имя процедуры - это имя типа данных, расширенное префиксом XDR xdr_ (например, xdr_int). Если тип данных определен в .x файле, rpcgen создает нужную процедуру xdr_. Если в исходном файле .x нет никакого определения типов данных (например, msg.x, выше), то файл _xdr.c не создается. Можно написать исходный файл .x, который использует тип данных, не поддерживаемый libnsl, и намеренно опустить определение типа (в файле .x). При этом нужно написать и процедуру xdr_. Это способ определения собственных процедур xdr_.

Серверная часть процедуры READDIR, в файле dir_proc.c:

/*

* dir_proc.c: удаленная реализация readdir

*/

#include <dirent.h>

#include "dir.h" /* Создается rpcgen */

 

extern int errno;

extern char *malloc();

extern char *strdup();

 

readdir_res *

readdir_1(nametype *dirname, struct svc_req *req)

{

  DIR *dirp;

  struct dirent *d;

  namelist nl;

  namelist *nlp;

  static readdir_res res; /* должен быть static! */

 

  /* Открыть каталог */

  dirp = opendir(*dirname);

  if (dirp == (DIR *)NULL) {

      res.errno = errno;

      return (&res);

  }

 

  /* Очистить предыдущий результат */

  xdr_free(xdr_readdir_res, &res);

  /*

  * Собрать элементы каталога.

  */

  nlp = &res.readdir_res_u.list;

  while (d = readdir(dirp)) {

      nl = *nlp = (namenode *)

      malloc(sizeof(namenode));

      if (nl == (namenode *) NULL) {

         res.errno = EAGAIN;

         closedir(dirp);

         return(&res);

      }

      nl->name = strdup(d->d_name);

      nlp = &nl->next;

  }

  *nlp = (namelist)NULL;

  /* Вывести результат */

  res.errno = 0;

  closedir(dirp);

  return (&res);

}

Клиентская часть процедуры READDIR, файл rls.c:

/*

* rls.c: Клиент для удаленного чтения каталогов

*/

#include <stdio.h>

#include "dir.h" /* создается rpcgen */

 

extern int errno;

 

main(int argc, char *argv[])

{

  CLIENT *clnt;

  char *server;

  char *dir;

  readdir_res *result;

  namelist nl;

  if (argc != 3) {

     fprintf(stderr, "usage: %s host

        directory\n",argv[0]);

     exit(1);

  }

  server = argv[1];

  dir = argv[2];

 

  /*

  * Создает обработчик клиента,

  * вызывающий MESSAGEPROG на сервере

  */

  cl = clnt_create(server, DIRPROG, DIRVERS, "tcp");

  if (clnt == (CLIENT *)NULL) {

      clnt_pcreateerror(server);

      exit(1);

  }

  result = readdir_1(&dir, clnt);

  if (result == (readdir_res *)NULL) {

      clnt_perror(clnt, server);

      exit(1);

  }

 

  /* Успешный вызов удаленной процедуры. */

  if (result->errno != 0) {

  /* Ошибка на удаленной системе.

  */

      errno = result->errno;

      perror(dir);

      exit(1);

  }

 

  /* Оглавление каталога получено.

  * Вывод на экран.

  */

  for (nl = result->readdir_res_u.list;

       nl != NULL;

       nl = nl->next) {

       printf("%s\n", nl->name);

  }

  xdr_free(xdr_readdir_res, result);

  clnt_destroy(cl);

  exit(0);

}

Код клиента, создаваемый rpcgen, не освобождает память, выделенную для результатов запроса RPC. Поэтому следует вызывать xdr_free(), чтобы освободить память после завершения работы. Это похоже на вызов free(), за исключением того, что здесь для получения результата передается процедура XDR.


next up previous contents
Next: Директивы препроцессора Up: Удаленный вызов процедур Previous: Преобразование локальных процедур в   Contents
2004-06-22