Beispiel für RPC-Callback-Prozeduren

In diesem Abschnitt werden die RPC-Beispiele (Remote Procedure Call) erläutert.

Gelegentlich ist es nützlich, einen Server als Client zu definieren und einen RPC an den Prozessclient zurückzunehmen. Beim fernen Debugging ist der Client beispielsweise ein Fenstersystemprogramm und der Server ist ein Debugger, der auf der fernen Maschine ausgeführt wird. Normalerweise klickt der Benutzer mit der Maustaste auf das Debugfenster. In diesem Schritt wird ein Debuggerbefehl aufgerufen, der einen Prozedurfernaufruf an den Server ausgibt (auf dem der Debugger tatsächlich ausgeführt wird) und ihm mitteilt, diesen Befehl auszuführen. Wenn der Debugger jedoch einen Unterbrechungspunkt erreicht, werden die Rollen umgekehrt. Der Debugger führt dann einen Prozedurfernaufruf an das Fensterprogramm aus, um den Benutzer zu informieren, dass ein Unterbrechungspunkt erreicht wurde.

Ein RPC-Callback erfordert eine Programmnummer, um den Fernprozeduraufruf durchzuführen. Da es sich hierbei um eine dynamisch generierte Programmnummer handelt, sollte sie im transienten Bereich 0x40000000 bis 0x5fffffffliegen. Die Routine gettransient gibt eine gültige Programmnummer im Übergangsbereich zurück und registriert sie beim Port-Mapper. Diese Routine kommuniziert nur mit dem Port-Mapper, der auf derselben Maschine wie die gettransient -Routine ausgeführt wird. Der Aufruf der Routine pmap_set ist eine Test-und-Set-Operation. Das heißt, es prüft unteilbar, ob eine Programmnummer bereits registriert wurde, und reserviert die Nummer, wenn dies nicht der Fall ist. Bei der RückkehrsockpDas Argument enthält einen Socket, der als Argument für eine svcudp_create -oder svctcp_create -Routine verwendet wird.


#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
gettransient(proto, vers, sockp)
      int proto, vers, *sockp;
{
      static int prognum = 0x40000000;
      int s, len, socktype;
      struct sockaddr_in addr;
      switch(proto) {
              case IPPROTO_UDP:
                      socktype = SOCK_DGRAM;
                      break;
              case IPPROTO_TCP:
                      socktype = SOCK_STREAM;
                      break;
              default:
                      fprintf(stderr, "unknown protocol type\n");
                      return 0;
      }
      if (*sockp == RPC_ANYSOCK) {
           if ((s = socket(AF_INET, socktype, 0)) < 0) {
                perror("socket");
                return (0);
           }
           *sockp = s;
      }
      else
           s = *sockp;
      addr.sin_addr.s_addr = 0;
      addr.sin_family = AF_INET;
      addr.sin_port = 0;
      len = sizeof(addr);
      /*
       * may be already bound, so don't check for error
       */
      bind(s, &addr, len);
      if (getsockname(s, &addr, &len)< 0) {
           perror("getsockname");
           return (0);
      }
      while (!pmap_set(prognum++, vers, proto, 
           ntohs(addr.sin_port))) continue;
      return (prognum-1);
}
Hinweis: Der Aufruf der Subroutine ntohs stellt sicher, dass die Portnummer inaddr.sin_port, die in Netzbyteanordnung angegeben ist, wird in Hostbyteanordnung übergeben. Die Subroutine pmap_set erwartet eine Host-Byteanordnung.

Die folgenden Programme veranschaulichen die Verwendung der Routine gettransient . Der Client führt einen Prozedurfernaufruf an den Server aus und übergibt ihm eine transiente Programmnummer. Anschließend wartet der Client auf einen Rückruf vom Server bei dieser Programmnummer. Der Server registriert das Programm EXAMPLEPROG , damit es den Fernprozeduraufruf empfangen kann, der es über die Nummer des Callback-Programms informiert. Dann sendet der Server zu einem zufällig ausgewählten Zeitpunkt (in diesem Beispiel beim Empfang des Signals SIGALRM ) einen Callback-Remote-Prozeduraufruf unter Verwendung der Programmnummer, die er zuvor empfangen hat.


/*
 * client
 */
#include <stdio.h>
#include <rpc/rpc.h>

int callback();
char hostname[256];

main()
{

      int x, ans, s;
      SVCXPRT *xprt;
      gethostname(hostname, sizeof(hostname));
      s = RPC_ANYSOCK;
      x = gettransient(IPPROTO_UDP, 1, &s);
      fprintf(stderr, "client gets prognum %d\n", x);
      if ((xprt = svcudp_create(s)) == NULL) {
        fprintf(stderr, "rpc_server: svcudp_create\n");
           exit(1);
      }
      /* protocol is 0 - gettransient does registering
       */
      (void)svc_register(xprt, x, 1, callback, 0);
      ans = callrpc(hostname, EXAMPLEPROG, EXAMPLEVERS,
           EXAMPLEPROC_CALLBACK, xdr_int, &x, xdr_void, 0);
      if ((enum clnt_stat) ans != RPC_SUCCESS) {
           fprintf(stderr, "call: ");
           clnt_perrno(ans);
           fprintf(stderr, "\n");
      }
      svc_run();
      fprintf(stderr, "Error: svc_run shouldn't return\n");

}

callback(rqstp, transp)
      register struct svc_req *rqstp;
      register SVCXPRT *transp;

{
      switch (rqstp->rq_proc) {
           case 0:
                if (!svc_sendreply(transp, xdr_void, 0)) {
                     fprintf(stderr, "err: exampleprog\n");
                     return (1);
                }
                return (0);
           case 1:
                if (!svc_getargs(transp, xdr_void, 0)) {
                     svcerr_decode(transp);
                     return (1);
                }
                fprintf(stderr, "client got callback\n");
                if (!svc_sendreply(transp, xdr_void, 0)) {
                     fprintf(stderr, "err: exampleprog");
                     return (1);
                }
      }

}
/*
 * server
 */
#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/signal.h>

char *getnewprog();
char hostname[256];
int docallback();
int pnum;          /*  program number for callback routine  */

main()

{
      gethostname(hostname, sizeof(hostname));
      registerrpc(EXAMPLEPROG, EXAMPLEVERS,
        EXAMPLEPROC_CALLBACK, getnewprog, xdr_int, xdr_void);
      fprintf(stderr, "server going into svc_run\n");
      signal(SIGALRM, docallback);
      alarm(10);
      svc_run();
      fprintf(stderr, "Error: svc_run shouldn't return\n");

}

char *
getnewprog(pnump)
     char *pnump;
{
     pnum = *(int *)pnump;
     return NULL;
}

docallback()
{
     int ans;

     ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0,
          xdr_void, 0);
     if (ans != 0) {
          fprintf(stderr, "server: ");
          clnt_perrno(ans);
          fprintf(stderr, "\n");
     }
}