Applicazioni MAT e CHAT in ONC_RPC di Annarita Fierro.

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]