ESEMPI DI APPLICAZIONI ONC-RPC • • Applicazione 1: Procedura remota per la somma di due numeri Applicazione 2: Procedura remota per la realizzazione di una chat Annarita Fierro matricola: 628404 Applicazione 1: ADD • Il programma prende in input due numeri (stringhe) da riga di comando, analizza il testo ASCII per convertirli in interi, quindi chiama una procedura remota per sommarli e restituisce il risultato Step 1: Creare l’IDL • Il primo passo consiste nella definizione dell'interfaccia che deve rispettare il formato di Sun per il suo Interface Definition Language (IDL) • Un IDL è un file (con suffisso .x) scritto in un linguaggio simile al C, l’RPCL (RPC Language) che fornisce le primitive per la definizione dei dati e delle procedure delle RPC • Un insieme di procedure remote sono raggruppate in una versione • Una o più versioni sono raggruppate in un programma File add.x • struct intpair { • • int a; int b; • Nell’esempio l’IDL prevede la definizione di una struct intpair che contiene due interi: essa rappresenta il parametro di input per la funzione add • }; • program ADD_PROG { version ADD_VERS { • int ADD(intpair) = 1; • } = 1; • } = 0x23451111; • • L’interfaccia avrà anche una versione e un programma. Occorre assegnare un ID ad ogni funzione, versione e programma • L’ID del programma è un numero a 32 bit. Sun riserva l'intervallo da 0 a 0x1fffffff Step 2: generare i file necessari • Per realizzare l’RPC è necessario creare il client stub e il server stub che si occupano di convertire i dati dal formato delle particolari macchine a un formato canonico (XDR) indipendente dall’architettura, implementando meccanismi di marshalling • Gli stub vengono generati automaticamente dal compilatore di protocollo con il comando: rpcgen –a –C add.x File generati • add.h File di intestazione incluso nel codice del client e del server. Definisce: • la struct intpair • ADD_PROG (0x23451111, il numero di programma) • ADD_VERS (1, il numero di versione) • l'interfaccia client stub (add_1) • l'interfaccia per la funzione server-side (add_1_svc) • add_svc.c Stub del server che implementa: • il main che registra il servizio • il listener per il programma: la funzione add_prog_1 (_1 utilizzato per distinguere il numero di versione) • switch per tutte le procedure remote supportate dal programma e dalla versione con case NULLPROC (che è sempre supportato), case ADD, che imposta un puntatore (local) per la funzione add_1_svc • add_clnt.c Stub del client che: • implementa la funzione add_1 • effettua il marshalling del parametro • chiama la procedura remota • restituisce il risultato • add_xdr.c • contiene il codice per il marshalling dei parametri per la struct intpair • utilizza le librerie XDR (External Data Representation) per convertire i due numeri interi in un formato standard • Makefile.add Utilizzato per compilare l'applicazione • add_server.c e add_client.c rpcgen genera il codice per il template del client e del server necessari per: • creare l’ RPC handle correttamente • chiamare correttamente le procedure Stub del Client: add_clnt.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include <memory.h> /* for memset */ #include "add.h" /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; int * add_1(intpair *argp, CLIENT *clnt) { static int clnt_res; memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call (clnt, ADD, (xdrproc_t) xdr_intpair, (caddr_t) argp, (xdrproc_t) xdr_int, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } Stub del Server: add_svc.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include "add.h" #include <stdio.h> #include <stdlib.h> #include <rpc/pmap_clnt.h> #include <string.h> #include <memory.h> #include <sys/socket.h> #include <netinet/in.h> #ifndef SIG_PF #define SIG_PF void(*)(int) #endif static void add_prog_1(struct svc_req *rqstp, register SVCXPRT *transp) { union { intpair add_1_arg; } argument; char *result; xdrproc_t _xdr_argument, _xdr_result; char *(*local)(char *, struct svc_req *); switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply (transp, (xdrproc_t) xdr_void,(char *)NULL); return; case ADD: _xdr_argument = (xdrproc_t) xdr_intpair; _xdr_result = (xdrproc_t) xdr_int; local = (char *(*)(char *, struct svc_req *)) add_1_svc; break; default: svcerr_noproc (transp); return; } memset ((char *)&argument, 0, sizeof (argument)); if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)){ svcerr_decode (transp); return; } result = (*local)((char *)&argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { svcerr_systemerr (transp); } if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { fprintf (stderr, "%s", "unable to free arguments"); exit (1); } return; } int main (int argc, char **argv) { register SVCXPRT *transp; pmap_unset (ADD_PROG, ADD_VERS); transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf (stderr, "%s", "cannot create udp service."); exit(1); } if (!svc_register(transp, ADD_PROG, ADD_VERS, add_prog_1, IPPROTO_UDP)) { fprintf (stderr, "%s", "unable to register (ADD_PROG, ADD_VERS, udp)."); exit(1); } transp = svctcp_create(RPC_ANYSOCK, 0, 0); if (transp == NULL) { fprintf (stderr, "%s", "cannot create tcp service."); exit(1); } if (!svc_register(transp, ADD_PROG, ADD_VERS, add_prog_1, IPPROTO_TCP)) { fprintf (stderr, "%s", "unable to register (ADD_PROG, ADD_VERS, tcp)."); exit(1); } svc_run (); fprintf (stderr, "%s", "svc_run returned"); exit (1); /* NOTREACHED */ } File add_xdr.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include "add.h" bool_t xdr_intpair (XDR *xdrs, intpair *objp) { register int32_t *buf; if (!xdr_int (xdrs, &objp->a)) return FALSE; if (!xdr_int (xdrs, &objp->b)) return FALSE; return TRUE; } Marshalling dei dati Step 4: Modifica di add_server.c • La funzione del server, contenuta in add_server.c va sostituita ai commenti • Il server riceve dal client due interi, visualizza la somma e la restituisce al client Step 5: Modifica di add_client.c /* #ifndef DEBUG * This is sample code generated by rpcgen. clnt_destroy (clnt); * These are only templates and you can use them #endif * as a guideline for developing your own functions. } /* DEBUG */ */ #include "add.h" int main (int argc, char *argv[]) void add_prog_1(char *host, int a, int b) { { char *host; CLIENT *clnt; int int a, b; *result_1; intpair add_1_arg; if (argc != 4) { printf ("usage: %s server_host num1 num2\n", argv[0]); #ifndef DEBUG exit(1); clnt = clnt_create (host, ADD_PROG, ADD_VERS, "udp"); } if (clnt == NULL) { host = argv[1]; clnt_pcreateerror (host); if ((a = atoi(argv[2])) == 0 && *argv[2] != '0') { exit (1); fprintf(stderr, "invalid value: %s\n", argv[2]); } #endif exit(1); /* DEBUG */ } add_1_arg.a = a; add_1_arg.b = b; if ((b = atoi(argv[3])) == 0 && *argv[3] != '0') { result_1 = add_1(&add_1_arg, clnt); fprintf(stderr, "invalid value: %s\n", argv[3]); if (result_1 == (int *) NULL) { exit(1); clnt_perror (clnt, "call failed"); } } add_prog_1(host, a, b); else { printf("Result = %d\n", *result_1); } } Step 6: Compilazione • Per compilare il codice si esegue il comando: make -f makefile.add • L’output è il seguente: Step 7: Esecuzione • Per eseguire il server si utilizza il comando: sudo ./add_server • Per eseguire il client si utilizza il comando: ./add_client localhost Applicazione 2: CHAT • Il programma consente lo scambio di messaggi tra client e server: il client si connette al server e gli invia un messaggio di testo; il server riceve il messaggio da parte del client e risponde. Lo scambio di messaggi termina quando il client digita la stringa «quit» File chat.x program MESSAGEPROG { version PRINTMESSAGEVERS { string PRINTMESSAGE(string) = 1; } = 1; } = 0x20000001; • Nell’esempio l’IDL prevede la definizione di una procedura che prende in input un array di char (string nel linguaggio RPCL) e restituisce un array di char • NB: a differenza del file add.x, che conteneva anche la definizione di una struct, in questo caso non sono presenti definizioni di variabili File generati • Il comando rpcgen –a –C chat.x genera i seguenti file: • NB: il file xdr.c non viene generato • chat.h File di intestazione incluso nel codice del client e del server. Definisce: • la funzione void LeggiStringa(char *buffer); • MESSAGEPROG (0x20000001, il numero di programma) • PRINTMESSAGEVERS (1, il numero di versione) • l'interfaccia client stub (printmessage_1) • l'interfaccia per la funzione server-side (printmessage_1_svc) • chat_svc.c Stub del server che implementa: • il main che registra il servizio • il listener per il programma: la funzione messageprog_1 (_1 utilizzato per distinguere il numero di versione) • switch per tutte le procedure remote supportate dal programma e dalla versione con case NULLPROC (che è sempre supportato), case PRINTMESSAGE, che imposta un puntatore (local) per la funzione printmessage_1_svc • chat_clnt.c Stub del client che: • implementa la funzione printmessage_1 • effettua il marshalling del parametro • chiama la procedura remota • restituisce il risultato • Makefile.chat Utilizzato per compilare l'applicazione • chat_server.c e chat_client.c rpcgen genera il codice per il template del client e del server necessari per: • creare l’ RPC handle correttamente • chiamare correttamente le procedure Stub del Client: chat_clnt.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include <memory.h> /* for memset */ #include "chat.h" /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; char ** printmessage_1(char **argp, CLIENT *clnt) { static char *clnt_res; memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call (clnt, PRINTMESSAGE, (xdrproc_t) xdr_wrapstring, (caddr_t) argp, (xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } Stub del Server: chat_svc.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include "chat.h" #include <stdio.h> #include <stdlib.h> #include <rpc/pmap_clnt.h> #include <string.h> #include <memory.h> #include <sys/socket.h> #include <netinet/in.h> #ifndef SIG_PF #define SIG_PF void(*)(int) #endif static void messageprog_1(struct svc_req *rqstp, register SVCXPRT *transp) { union { char *printmessage_1_arg; } argument; char *result; xdrproc_t _xdr_argument, _xdr_result; char *(*local)(char *, struct svc_req *); switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); return; case PRINTMESSAGE: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_wrapstring; local = (char *(*)(char *, struct svc_req *)) printmessage_1_svc; break; default: svcerr_noproc (transp); return; } memset ((char *)&argument, 0, sizeof (argument)); if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { svcerr_decode (transp); return; } result = (*local)((char *)&argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { svcerr_systemerr (transp); } if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { fprintf (stderr, "%s", "unable to free arguments"); exit (1); } return; } int main (int argc, char **argv) { register SVCXPRT *transp; pmap_unset (MESSAGEPROG, PRINTMESSAGEVERS); transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf (stderr, "%s", "cannot create udp service."); exit(1); } if (!svc_register(transp, MESSAGEPROG, PRINTMESSAGEVERS, messageprog_1, IPPROTO_UDP)) { fprintf (stderr, "%s", "unable to register (MESSAGEPROG, PRINTMESSAGEVERS, udp)."); exit(1); } transp = svctcp_create(RPC_ANYSOCK, 0, 0); if (transp == NULL) { fprintf (stderr, "%s", "cannot create tcp service."); exit(1); } if (!svc_register(transp, MESSAGEPROG, PRINTMESSAGEVERS, messageprog_1, IPPROTO_TCP)) { fprintf (stderr, "%s", "unable to register (MESSAGEPROG, PRINTMESSAGEVERS, tcp)."); exit(1); } svc_run (); fprintf (stderr, "%s", "svc_run returned"); exit (1); /* NOTREACHED */ Modifica di chat_server.c • La funzione del server, contenuta in chat_server.c va sostituita ai commenti Trasmissione per copia/ripristino Modifica di chat_client.c #include "chat.h" void messageprog_1(char *host, char *msg) if (argc < 1) { { printf ("usage: %s server_host\n", argv[0]); CLIENT *clnt; exit (1); char * *result_1; } #ifndef DEBUG host = argv[1]; clnt = clnt_create (host, MESSAGEPROG, PRINTMESSAGEVERS, "tcp"); printf("\nInizia una conversazione [digita \"quit\" per uscire]:\n\n"); if (clnt == NULL) { while (TRUE) clnt_pcreateerror (host); { exit (1); printf("Me: "); } LeggiStringa(temp); #endif if(strcmp(quit, temp) == 0) result_1 = printmessage_1(&msg, clnt); break; if (result_1 == (char **) NULL) { messaggio = strdup(temp); clnt_perror (clnt, "call failed"); messageprog_1 (host, messaggio); }else { }; printf("Server: %s\n" , *result_1 ); printf("\nUscita..\n"); } #ifndef DEBUG exit (0); } clnt_destroy (clnt); #endif void LeggiStringa(char *messaggio) } { int main (int argc, char *argv[]) { int i = 0; char ch; char *host; char *messaggio; while ( (ch = getchar()) != '\n') { messaggio[i++] = ch;} char temp[100]; messaggio[i] = '\0'; //Carattere di fine stringa char *quit = "quit"; fflush(stdout); fflush(stdin); Compilazione • Come già visto, per compilare il codice si esegue il comando: make -f Makefile.chat • L’output è il seguente: Esecuzione • Per eseguire il server si utilizza il comando: sudo ./chat_server • Per eseguire il client si utilizza il comando: ./chat_client localhost Bibliografia • Lucidi delle lezioni [Socket RPC e RMI] • Introduction to programming with Sun/ONC RPC [http://www.cs.rutgers.edu/~pxk/rutgers/notes/rpc/] • Writing RPC Applications with the rpcgen Protocol Compiler [http://neo.dmcs.pl/rso/du/onc-rpc3.html] • rpc programming with rpcgen in c part2 [http://www.youtube.com/watch?v=Z_VR8sxIBkY] • ONC+ Developer's Guide [https://docs.oracle.com/cd/E19683-01/8161435/rpcgenpguide-21470/index.html] • Chapter 4. Programming with rpcgen [http://techpubs.sgi.com/library/dynaweb_docs/0620/SGI_ Developer/books/IRIX_NetPG/sgi_html/ch04.html]
© Copyright 2024 Paperzz