生成 XDR 例程示例

本节说明生成 XDR 例程示例的过程。

将本地过程转换为远程过程示例 演示自动生成客户机和服务器远程过程调用 (RPC) 代码。 rpcgen 协议编译器还可用于生成 eXternal 数据表示 (XDR) 例程,这些例程将本地数据结构转换为网络格式,反之亦然。 以下协议描述文件提供了一个完整的 RPC 服务,该服务是一个远程目录列表服务,该服务使用 rpcgen 协议编译器不仅生成存根例程,还生成 XDR 例程。


/*
 * dir.x: Remote directory listing protocol
 */
 const MAXNAMELEN = 255;/* maximum length of a directory entry */
 typedef string nametype<MAXNAMELEN>;    /* a directory entry */
 typedef struct namenode *namelist;   /* a link in the listing */
 /*
  * A node in the directory listing 
  */
 struct namenode {
      nametype name;      /* name of directory entry */
      namelist next;      /* next entry */
 };
 /*
  * The result of a READDIR operation.
  */
  union readdir_res switch (int errno) { 
  case 0:
      namelist list;  /* no error: return directory listing */
  default:
      void;        /* error occurred: nothing else to return */
 };
 /*
  * The directory program definition
  */
 program DIRPROG { 
      version DIRVERS { 
              readdir_res 
              READDIR(nametype) = 1;
         } = 1;
 } = 76;
 
注: 类型 (如readdir_res在先前示例中,可以使用 structunionenum 关键字来定义) ,但不要在这些类型的变量的后续声明中使用这些关键字。 例如,如果定义并集,foo,仅使用foo而不是union foo. 实际上, rpcgen 协议编译器将 RPC 并集编译为 C 结构,在这种情况下,使用 并集 关键字声明这些并集是错误的。

dir.x 文件上运行 rpcgen 协议编译器将创建四个输出文件。 三个与之前相同: 头文件,客户机存根例程和服务器框架。 第四个文件包含将指定的数据类型转换为 XDR 格式所需的 XDR 例程,反之亦然。 这些是 dir_xdr.c 文件中的输出。

以下是 READDIR 过程的实现:

/*
 * dir_proc.c: remote readdir implementation
 */
 #include <rpc/rpc.h>
 #include <sys/dir.h>
 #include "dir.h"
 extern int errno;
 extern char *malloc();
 extern char *strdup();
 readdir_res *
 readdir_1(dirname)
         nametype *dirname;
 {
         DIR *dirp;
         struct direct *d;
         namelist nl;
         namelist *nlp;
         static readdir_res res; /* must be static */
         /*
          * Open directory
          */
         dirp = opendir(*dirname);
         if (dirp == NULL) {
                 res.errno = errno;
                 return (&res);
         }
         /* 
          * Free previous result
          */
         xdr_free(xdr_readdir_res, &res);
         /*
          * Collect directory entries.
          * Memory allocated here will be freed by xdr_free
          * next time readdir_1 is called
          */ 
         nlp = &res.readdir_res_u.list;
         while (d = readdir(dirp)) {
            nl = *nlp = (namenode *) malloc(sizeof(namenode));
            nl->name = strdup(d->d_name);
            nlp = &nl->next; 
         }
          *nlp = NULL;
       
          /*
          * Return the result
          */
          res.errno = 0;
         closedir(dirp);
         return (&res);
 }
客户机端程序按如下所示调用服务器:

/*
 * rls.c: Remote directory listing client
 */
 #include <stdio.h>
 #include <rpc/rpc.h>    /* always need this */
 #include "dir.h"        /* will be generated by rpcgen */
 extern int errno;
 main(argc, argv)
         int argc;
         char *argv[];
 {
         CLIENT *cl;
         char *server;
         char *dir;
         readdir_res *result;
         namelist nl;
         if (argc != 3) { 
                fprintf(stderr, "usage: %s host directory\n",
                   argv[0]);
                 exit(1);
         }
         /*
          * Remember what our command line arguments refer to
          */
         server = argv[1];
         dir = argv[2];

         /*
          * Create client "handle" used for calling MESSAGEPROG
          * on the server designated on the command line. We
          * tell the RPC package to use the "tcp" protocol
          * when contacting the server.
          */
         cl = clnt_create(server, DIRPROG, DIRVERS, "tcp");
         if (cl == NULL) {
              /*
               * Could not establish connection with server.
               * Print error message and die.
               */
               clnt_pcreateerror(server);
               exit(1);

         }

         /*
          * Call the remote procedure readdir on the server
          */
         result = readdir_1(&dir, cl);
         if (result == NULL) {
                 /*
                  * An error occurred while calling the server.
                  * Print error message and die.
                  */
                 clnt_perror(cl, server);
                 exit(1);
         }
         /*
          * Okay, we successfully called the remote procedure.
          */
         if (result->errno != 0) { 
                /*
                 * A remote system error occurred.
                 * Print error message and die.
                 */
                 errno = result->errno;
                 perror(dir);
                 exit(1);
         }
         /*
          * Successfully got a directory listing.
          * Print it out.
          */
         for (nl = result->readdir_res_u.list; nl != NULL;
           nl = nl->next) {
                 printf("%s\en", nl->name);
         }
         exit(0);
 }  

最后,对于 rpcgen 协议编译器,可以将客户机程序和服务器过程作为单个程序一起测试,方法是将它们相互链接,而不是与客户机和服务器存根链接。 过程调用作为普通本地过程调用执行,并且可以使用本地调试器 (例如 dbx) 来调试程序。 当程序工作时,可以将客户机程序链接到由 rpcgen 协议编译器生成的客户机存根。 可以将服务器过程链接到 rpcgen 协议编译器生成的服务器存根。

注: 如果执行此操作,那么您可能想要注释掉对 RPC 库例程的调用,并让客户机端例程直接调用服务器例程。