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