Exemplo de uso de RPC
Em RPC, o único vínculo explícito entre o servidor e seus clientes é a especificação da interface do serviço oferecido. Essa especificação segue um formato relativamente simples. O exemplo a seguir define um serviço composto de duas operações: ADD e SUB:
- addsub.x
/* addsub.x : definição da interface */ #define PROGRAM_NUMBER 12345678 #define VERSION_NUMBER 1 /* tipo de dado que será passado aos procedimentos remotos */ struct operands { int x; int y; }; /* Definição da interface que será oferecida aos clientes */ program ADDSUB_PROG { version ADDSUB_VERSION { int ADD (operands) = 1; int SUB (operands) = 2; } = VERSION_NUMBER; } = PROGRAM_NUMBER;
Essa definição de interface deve ser compilada pelo utilitário rpcgen
, gerando diversos arquivos de código e cabeçalho que serão compilados juntamente com o cliente e o servidor:
espec:rpc> rpcgen -C addsub.x espec:rpc> ll total 28 -rw-r--r-- 1 prof 842 Out 16 21:36 addsub_clnt.c -rw-r--r-- 1 prof 1113 Out 16 21:36 addsub.h -rw-r--r-- 1 prof 2360 Out 16 21:36 addsub_svc.c -rw-r--r-- 1 prof 423 Out 16 21:34 addsub.x -rw-r--r-- 1 prof 287 Out 16 21:36 addsub_xdr.c
A seguir devem ser escritos o cliente e o servidor que irão se comunicar usando essa especificação. Como o servidor é passivo e só aguarda, processa e responde pedidos, seu código é bastante simples:
- server.c
/* Arquivo server.c: um servidor RPC simples */ #include <stdio.h> #include "addsub.h" /* implementação da função add */ int * add_1_svc (operands *argp, struct svc_req *rqstp) { static int result; printf ("Recebi chamado: add %d %d\n", argp->x, argp->y); result = argp->x + argp->y; return (&result); } /* implementação da função sub */ int * sub_1_svc (operands *argp, struct svc_req *rqstp) { static int result; printf ("Recebi chamado: sub %d %d\n", argp->x, argp->y); result = argp->x - argp->y; return (&result); }
O código do cliente é um pouco mais complexo, pois ele precisa obter uma referência do servidor antes de submeter seus pedidos:
- client.c
/* Arquivo client.c: um cliente RPC simples */ #include <stdio.h> #include "addsub.h" /* função que chama a RPC add_1 */ int add (CLIENT *clnt, int x, int y) { operands ops; int *result; /* junta os parâmetros em um struct */ ops.x = x; ops.y = y; /* chama a função remota */ result = add_1 (&ops,clnt); if (result == NULL) { printf ("Problemas ao chamar a função remota\n"); exit (1); } return (*result); } /* função que chama a RPC sub_1 */ int sub (CLIENT *clnt, int x, int y) { operands ops; int *result; /* junta os parâmetros em um struct */ ops.x = x; ops.y = y; /* chama a função remota */ result = sub_1 (&ops,clnt); if (result == NULL) { printf ("Problemas ao chamar a função remota\n"); exit (1); } return (*result); } int main( int argc, char *argv[]) { CLIENT *clnt; int x,y; /* verifica se o cliente foi chamado corretamente */ if (argc!=4) { fprintf (stderr,"Usage: %s hostname num1 num2\n",argv[0]); exit (1); } /* cria uma struct CLIENT que referencia uma interface RPC */ clnt = clnt_create (argv[1], ADDSUB_PROG, ADDSUB_VERSION, "udp"); /* verifica se a referência foi criada */ if (clnt == (CLIENT *) NULL) { clnt_pcreateerror (argv[1]); exit(1); } /* obtém os dois inteiros que serão passados via RPC */ x = atoi (argv[2]); y = atoi (argv[3]); /* executa os procedimentos remotos */ printf ("%d + %d = %d\n", x, y, add (clnt,x,y)); printf ("%d - %d = %d\n", x, y, sub (clnt,x,y)); return (0); }
Em seguida, cliente e servidor podem ser compilados separadamente, para gerar os executáveis client
e server
:
$ cc server.c addsub_svc.c addsub_xdr.c -o server -lnsl $ cc client.c addsub_clnt.c addsub_xdr.c -o client -lnsl $ ll total 60 -rw-r--r-- 1 prof 842 Out 16 21:36 addsub_clnt.c -rw-r--r-- 1 prof 1113 Out 16 21:36 addsub.h -rw-r--r-- 1 prof 2360 Out 16 21:36 addsub_svc.c -rw-r--r-- 1 prof 423 Out 16 21:34 addsub.x -rw-r--r-- 1 prof 287 Out 16 21:36 addsub_xdr.c -rwxr-xr-x 1 prof 13523 Out 16 21:37 client -rw-r--r-- 1 prof 1583 Out 16 21:29 client.c -rwxr-xr-x 1 prof 14598 Out 16 21:37 server -rw-r--r-- 1 prof 507 Out 16 21:28 server.c
O servidor (arquivo server
) deve ser lançado antes do cliente (arquivo client
), de preferência em uma janela separada.