Beispiel für das Generieren von XDR-Routinen

In diesem Abschnitt wird die Prozedur zum Generieren eines Beispiels für eine XDR-Routine erläutert.

Das Beispiel für die Konvertierung lokaler Prozeduren in ferne Prozeduren veranschaulicht die automatische Generierung von Client-und Server-RPC-Code (RPC = Remote Procedure Call). Der Protokollcompiler rpcgen kann auch verwendet werden, um eXternal Datendarstellungsroutinen (XDR) zu generieren, die lokale Datenstrukturen in das Netzformat konvertieren und umgekehrt. Die folgende Protokollbeschreibungsdatei stellt einen vollständigen RPC-Service dar, der ein ferner Verzeichnislistenservice ist, der den Protokollcompiler rpcgen verwendet, um nicht nur Stubroutinen, sondern auch XDR-Routinen zu generieren.


/*
 * 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;
 
Hinweis: Typen (z. B.readdir_resIm vorherigen Beispiel) können mithilfe der Schlüsselwörter struct, union und enum definiert werden, verwenden diese Schlüsselwörter jedoch nicht in nachfolgenden Deklarationen von Variablen dieser Typen. Wenn Sie beispielsweise eine Union definieren,foo, deklarieren Sie es nur unter Verwendung vonfoound nichtunion foo. Tatsächlich kompiliert der rpcgen -Protokollcompiler RPC-Unions in C-Strukturen. In diesem Fall ist es ein Fehler, diese Unions mit dem Schlüsselwort union zu deklarieren.

Wenn Sie den rpcgen -Protokollcompiler für die Datei dir.x ausführen, werden vier Ausgabedateien erstellt. Drei sind dieselben wie zuvor: Headerdatei, Client-Stub-Routinen und Servergerüst. Die vierte Datei enthält die XDR-Routinen, die für die Konvertierung der angegebenen Datentypen in das XDR-Format erforderlich sind, und umgekehrt. Diese werden in der Datei dir_xdr.c ausgegeben.

Im Folgenden sehen Sie die Implementierung der Prozedur 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);
 }
Das clientseitige Programm ruft den Server wie folgt auf:

/*
 * 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);
 }  

Im Hinblick auf den rpcgen -Protokollcompiler können das Clientprogramm und die Serverprozedur zusammen als ein einziges Programm getestet werden, indem sie miteinander verknüpft werden und nicht mit Client-und Server-Stubs. Die Prozeduraufrufe werden als normale lokale Prozeduraufrufe ausgeführt und das Programm kann mit einem lokalen Debugger wie dbxdebuggt werden. Wenn das Programm funktioniert, kann es mit dem Client-Stub verbunden werden, der vom rpcgen -Protokollcompiler erzeugt wird. Die Serverprozeduren können mit dem vom rpcgen -Protokollcompiler erstellten Server-Stub verbunden werden.

Hinweis: In diesem Fall können Sie Aufrufe von RPC-Bibliotheksroutinen auf Kommentar setzen und Serverroutinen direkt von clientseitigen Routinen aufrufen lassen.