RPC 回调过程示例

本节说明远程过程调用 (RPC) 示例。

有时,使服务器成为客户机并使 RPC 返回到进程客户机很有用。 例如,通过远程调试,客户机是窗口系统程序,而服务器是在远程机器上运行的调试器。 通常,用户在调试窗口单击鼠标按键。 此步骤调用调试器命令,该命令对服务器 (其中调试器实际正在运行) 进行远程过程调用,使其执行该命令。 但是,当调试器命中断点时,将撤销角色。 然后调试器对窗口程序进行远程过程调用,以通知用户已到达断点。

RPC 回调需要程序号才能进行远程过程调用。 因为这将是动态生成的程序号,所以它应该在瞬态范围 0x40000000 到 0x5fffffff中。 get瞬态 例程返回瞬态范围内的有效程序号,并向端口映射器注册该程序号。 此例程仅与在与 get瞬态 例程本身相同的机器上运行的端口映射器进行通信。 对 pmap_set 例程的调用是测试和设置操作。 即,它不可分割地测试一个程序号是否已经注册,如果没有,则保留该号。 返回时,sockp自变量包含可用作 svcudp_createsvctcp_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);
}
注:ntohs 子例程的调用确保端口号在addr.sin_port,按网络字节顺序,按主机字节顺序传递。 pmap_set 子例程期望主机字节顺序。

以下程序说明如何使用 get瞬态 例程。 客户机对服务器进行远程过程调用,向其传递瞬态程序号。 然后,客户机等待从该程序号处的服务器接收回调。 服务器注册 EXAMPLEPROG 程序,以便它可以接收通知它回调程序号的远程过程调用。 然后,在某个随机选择的时间 (在此示例中接收到 SIGALRM 信号时) ,服务器使用其先前接收到的程序号发送回调远程过程调用。


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