RPC 回调过程示例
本节说明远程过程调用 (RPC) 示例。
有时,使服务器成为客户机并使 RPC 返回到进程客户机很有用。 例如,通过远程调试,客户机是窗口系统程序,而服务器是在远程机器上运行的调试器。 通常,用户在调试窗口单击鼠标按键。 此步骤调用调试器命令,该命令对服务器 (其中调试器实际正在运行) 进行远程过程调用,使其执行该命令。 但是,当调试器命中断点时,将撤销角色。 然后调试器对窗口程序进行远程过程调用,以通知用户已到达断点。
RPC 回调需要程序号才能进行远程过程调用。 因为这将是动态生成的程序号,所以它应该在瞬态范围 0x40000000 到 0x5fffffff中。 get瞬态 例程返回瞬态范围内的有效程序号,并向端口映射器注册该程序号。 此例程仅与在与 get瞬态 例程本身相同的机器上运行的端口映射器进行通信。 对 pmap_set 例程的调用是测试和设置操作。 即,它不可分割地测试一个程序号是否已经注册,如果没有,则保留该号。 返回时,sockp自变量包含可用作 svcudp_create 或 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);
}
注: 对 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");
}
}