RPCGEN: The Protocol Compiler
Listing 1: Source for avg.x
/* * The average procedure receives * an array of real numbers and * returns the average of their * values. This toy service * handles a maximum of * 200 numbers. */ const MAXAVGSIZE = 200; struct input_data { double input_data<200>; }; typedef struct input_data input_data; program AVERAGEPROG { version AVERAGEVERS { double AVERAGE(input_data) = 1; } = 1; } = 22855;
We can store this source code in a file called avg.x and invoke rpcgen with the following command:
rpcgen avg.x
Obtain the header file avg.h shown in Listing 2.
Listing 2: Header File avg.h
/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _AVG_H_RPCGEN #define _AVG_H_RPCGEN #include#define MAXAVGSIZE 200 struct input_data { struct { u_int input_data_len; double *input_data_val; } input_data; }; typedef struct input_data input_data; #ifdef __cplusplus extern "C" bool_t xdr_input_data(XDR *, input_data*); #elif __STDC__ extern bool_t xdr_input_data(XDR *, input_data*); #else /* Old Style C */ bool_t xdr_input_data(); #endif /* Old Style C */ #ifdef __cplusplus extern "C" bool_t xdr_input_data(XDR *, input_data*); #elif __STDC__
extern bool_t xdr_input_data(XDR *, input_data*); #else /* Old Style C */ bool_t xdr_input_data(); #endif /* Old Style C */ #define AVERAGEPROG ((u_long)22855) #define AVERAGEVERS ((u_long)1) #ifdef __cplusplus #define AVERAGE ((u_long)1) extern "C" double * average_1(input_data *, CLIENT *); extern "C" double * average_1_svc(input_data *, struct svc_req *); #elif __STDC__ #define AVERAGE ((u_long)1) extern double * average_1(input_data *, CLIENT *); extern double * average_1_svc(input_data *, struct svc_req *); #else /* Old Style C */ #define AVERAGE ((u_long)1) extern double * average_1(); extern double * average_1_svc(); #endif /* Old Style C */ #endif /* !_AVG_H_RPCGEN */
This file contains all of the function prototypes and data declarations needed for the development of our application. It will also generate three other source files:
-
avg_clnt.c: The stub program for our client (caller) process
-
avg_svc.c: The main program for our server (callee) process
-
avg_xdr.c: The XDR routines used by both the client and the server
These sources are to be used "as is" and must not be edited.
To complete the application at the server end, we need code to provide the actual "smarts" required to correctly process the input data. This must be created manually. The code for the sample application presented here is shown in Listing 3. This code takes the XDR-decoded array from the client and separates and averages the values. It returns the result, which is then XDR-encoded for transmission back to the client.
Listing 3: Server Code for Average Application
#include#include "avg.h" #include static double sum_avg; double * average_1(input_data *input, CLIENT *client) { double *dp = input->input_data.input_data_val; u_int i; sum_avg = 0; for(i=1;i<=input->input_data.input_data_len;i++) { sum_avg = sum_avg + *dp; dp++; } sum_avg = sum_avg / input->input_data.input_data_len; return(&sum_avg); } double * average_1_svc(input_data *input, struct svc_req *svc) { CLIENT *client; return(average_1(input,client)); }
To complete the application at the client end, the input data must be packed into XDR format so that it can be sent to the server. The client program is also generated manually and is shown in Listing 4. The Makefile shown in Listing 5 can be used to build the application.
Listing 4: Client Code for Average Application
#include "avg.h" #includevoid averageprog_1( char* host, int argc, char *argv[]) { CLIENT *clnt; double *result_1, *dp, f; char *endptr; int i; input_data average_1_arg; average_1_arg.input_data.input_data_val = (double*) malloc(MAXAVGSIZE*sizeof(double)); dp = average_1_arg.input_data.input_data_val; average_1_arg.input_data.input_data_len = argc - 2; for (i=1;i<=(argc - 2);i++) { f = strtod(argv[i+1],&endptr); printf("value = %e\n",f); *dp = f; dp++; } clnt = clnt_create(host, AVERAGEPROG, AVERAGEVERS, "udp"); if (clnt == NULL) { clnt_pcreateerror(host); exit(1); } result_1 = average_1(&average_1_arg, clnt); if (result_1 == NULL) { clnt_perror(clnt, "call failed:"); } clnt_destroy( clnt ); printf("average = %e\n",*result_1); } main( int argc, char* argv[] ) { char *host; if(argc < 3) { printf( "usage: %s server_host value ...\n", argv[0]); exit(1); } if(argc > MAXAVGSIZE + 2) { printf("Two many input values\n"); exit(2); } host = argv[1]; averageprog_1( host, argc, argv); }
Listing 5: Makefile
BIN = ravg avg_svc GEN = avg_clnt.c avg_svc.c avg_xdr.c avg.h RPCCOM = rpcgen all: $(BIN) ravg: ravg.o avg_clnt.o avg_xdr.o $(CC) -o $@ ravg.o avg_clnt.o avg_xdr.o ravg.o: ravg.c avg.h $(CC) -g ravg.c -c avg_svc: avg_proc.o avg_svc.o avg_xdr.o $(CC) -o $@ avg_proc.o avg_svc.o avg_xdr.o<\n> avg_proc.o: avg_proc.c avg.h $(GEN): avg.x $(RPCCOM) avg.x
clean cleanup:
rm -f $(GEN) *.o $(BIN)