Exemplo De Procedimentos De Callback RPC

Esta seção explica os exemplos de Chamada de Procedimento Remoto (RPC).

Ocasionalmente, é útil ter um servidor se tornar um cliente e fazer uma RPC de volta para o cliente do processo. Por exemplo, com a depuração remota, o cliente é um programa de sistema de janela e o servidor é um depurador rodando na máquina remota. Geralmente, o usuário clica em um botão de mouse na janela de depuração. Esta etapa invoca um comando debugger que faz uma chamada de procedimento remoto para o servidor (onde o depurador está realmente em execução), contando para executar esse comando. Quando o depurador atinge um ponto de ruptura, no entanto, as funções são invertidas. O depurador então faz uma chamada de procedimento remoto para o programa de janela para informar ao usuário que um ponto de interrupção foi atingido.

Um callback da RPC requer um número de programa para fazer a chamada de procedimento remoto. Como este será um número de programa gerado dinamicamente, ele deverá estar na faixa transitória, 0x40000000 a 0x5fffffff. A rotina gettransient retorna um número de programa válido na faixa transiente, e registra-o com a mapper de porta. Esta rotina conversa apenas com o mapper de porta rodando na mesma máquina da própria rotina gettransient . A chamada para a rotina pmap_set é uma operação de teste-e-set. Ou seja, ele faz testes indivisivelmente se um número de programa já foi registrado, e reserva o número se não tiver. No retorno, osockpargumento contém um socket que pode ser usado como o argumento para uma rotina svcudp_create ou svctcp_create .


#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);
}
Nota: A chamada para a subroutine ntohs garante que o número da porta emaddr.sin_port, que está em ordem de byte de rede, é passada em ordem de byte host. A subroutine pmap_set espera ordem de byte host.

Os programas a seguir ilustram como usar a rotina gettransient . O cliente faz uma chamada de procedimento remoto para o servidor, passando-o de um número de programa transiente. Em seguida, o cliente aguarda por perto para receber um callback do servidor naquele número do programa. O servidor registra o programa EXAMPLEPROG para que ele possa receber a chamada de procedimento remoto informando-o do número do programa de callback. Em seguida, em algum momento selecionado aleatoriamente (ao receber um sinal SIGALRM neste exemplo), o servidor envia uma chamada de procedimento remoto de callback, usando o número do programa que recebeu anteriormente.


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