Remote Procedure Calls
Definition:
RPCs translate local procedure calls to remote procedure calls in a way transparent to the calling procedure.
Transparency
To be transparent to client and servers routines, RPCs use stubs:
Client Stubs are local procedures that are called by clients and handle all packaging of arguments and server communication.
It appears to the client that the client stub's procedure is the actual server procedure it wants to call.
Server Stubs are local procedures that wait for client requests, handle packaging of arguments and call servers' local procedures (that implement the server's service).
It appears to the server that the server stub's call is the actual client call it needs to serve.
Parameter Passing:
To make transparent parameter passing between client and servers procedures, stubs need to:
- Pack and unpack procedures parameters for transmission over the network.
- Convert the representation of parameters from one machine to another.
A Remote Procedure Call
Steps of a RPC:
Client procedure calls the client stub in the normal way.
Client stub builds a message and calls the kernel.
Kernel sends the message to the kernel in a remote machine.
Remote kernel passes the message to the server stub.
Server stub unpacks the parameters and calls the server procedure.
Server procedure does the work and returns the results to the stub.
Server stub packs it in a message and calls the remote kernel.
Remote kernel sends the message to the client's kernel.
Kernel passes the message to the client stub.
Client stub unpack the result and returns it to the client procedure.
Client procedure gets the result in the normal way.
Sun RPC
Sun RPC is the most used implementation of RPCs.
It has four parts:
∙ rpcgen - A compiler that takes the definition of a remote procedure interface, and generates the client and server stubs.
∙ XDR (eXternal Data Representation), a standard way of encoding data in a portable fashion between different systems.
∙ A daemon to bind program numbers and versions to UDP ports.
∙ A run-time library to handle all the details.
∙rpcgen
operation:
+------------+ +-----------+
| dateproc.c |------------------------------------------->cc-->| date_svc |
+------------+ / | +-----------+
/ |
server stub / |
+-------------+ / |
| date_svc.c |/ |
/ +-------------+ |
/ |
/ +--------+
specification RPC / | |
+------------+ / +-------------+ | RPC |
| date.x |--> rpcgen ----->| date.h | |run-time|
+------------+ \ +-------------+ |library |
\ | |
\ +--------+
\ |
\ +-------------+ |
| date_clnt.c |\ |
+-------------+ \ |
client stub \ |
client \ | client prg.
+------------+ \ | +----------+
| rdate.c |------------------------------------------> cc -->| rdate |
+------------+ +----------+Example:
program DATE_PROG {
version DATE_VERS {
long BIN_DATE(void) = 1; /* Procedure number 1 */
string STR_DATE(long) = 2; /* Procedure number 2 */
} = 1; /* Version number 1 */
} = 0x31234567; /* Program number 0x31234567 */The program numbers are 32-bit integers assigned as follows:
Program number:
0x00000000 - 0x1fffffff defined by Sun 0x20000000 - 0x3fffffff defined by users 0x40000000 - 0x5fffffff transient 0x60000000 - 0xffffffff reserved
Note:
Procedure numbers start at 0. Every remote program and version must define procedure number 0 as the "null procedure".
It is automatically generated by the rpcgen compiler to allow a client to call it, to verify that a particular program version exist.
∙Binding
The portmapper daemon on the remote server is contacted to locate a specific program and version:
Writing a RPC Client and Server
The Time Server
The Time server serves the computer's local time to RPC requests.
It has two procedures:
bin_time - Returns the current time as a long int.
str_time - Convert the time from long int to string.
rpcgen can generate all the necessary files to compile a RPC application. Using the -a flag, rpcgen will generate sample files and a makefile for the application.
The time.xdr file describes the procedures:
program TIME_PROG {
version TIME_VERS {
long BIN_TIME(void) = 1; /* Procedure number 1 */
string STR_TIME(long) = 2; /* Procedure number 2 */
} = 1; /* Version number 1 */
} = 0x31234567; /* Program number 0x31234567 */The following command does the compilation (the -C flag generates ANSI C code):
rpcgen -a -C time.xdr
rpcgen generates 6 files:
time.h - To be included by all files.
time_clnt.c - Stubs for the client.
time_client.c - Sample file for the client.
time_svc.c - Stubs for the server.
time_server.c - Sample file for the server.
makefile.time - Makefile to compile the application.
∙ Writing the server procedures:
This is the sample generated by rpcgen:
long * bin_time_1_svc(void *argp, struct svc_req *rqstp)
{
static long result;
/* insert server code here */
return (&result);
}
char ** str_time_1_svc(long *argp, struct svc_req *rqstp)
{
static char * result;
/* insert server code here*/
return (&result);
}This is the modified program:
long * bin_time_1_svc(void *argp, struct svc_req *rqstp)
{
static long result;
result= time((time_t *) 0);
return (&result);
}
char ** str_time_1_svc(long *argp, struct svc_req *rqstp)
{
static char * result;
static tstr[128];
long t;
t= *argp;
sec= t % 60; t /= 60;
min= t % 60; t /= 60;
hour= t % 24;
sprintf(tstr, "Time %u:%u:%u", hour, min, sec);
result= tstr;
return (&result);
}∙The Time client
This is the sample from rpcgen:
void time_prog_1(char *host)
{
...
clnt = clnt_create(host, TIME_PROG, TIME_VERS, "netpath");
...
result_1 = bin_time_1((void *)&bin_time_1_arg, clnt);
if (result_1 == (long *) NULL)
clnt_perror(clnt, "call failed");
result_2 = str_time_1(&str_time_1_arg, clnt);
if (result_2 == (char **) NULL)
clnt_perror(clnt, "call failed");
clnt_destroy(clnt);
}
main(int argc, char *argv[])
{
time_prog_1(argv[1]);
}This is the modified program:
void time_prog_1(char *host)
{
...
result_1 = bin_date_1((void *)&bin_date_1_arg, clnt);
if (result_1 == (long *) NULL)
clnt_perror(clnt, "call failed");
result_2 = str_date_1( (long *) result_1, clnt);
if (result_2 == (char **) NULL)
clnt_perror(clnt, "call failed");
printf("Time: %u %s\n", *result_1, *result_2);
}
...∙ Running the client:
Time: 862359655 0:20:55
RPC call interface
RPC has multiple levels of interface to its services. These levels provide different degrees of control balanced with different levels of complexity:
Simplified interface.
Top level.
Intermediate level.
Expert level.
Bottom level.
Simplified Interface:
The simplified interface (the highest application level) is very simple, but limits control of the underlying communications mechanisms. It specifies only the type of transport to use.
Advantages:
Simplicity.
Program development can be rapid.
It is directly supported by the rpcgen compiler.
It is sufficient for most applications.
RPC routines:
rpc_reg(): Registers a procedure as an RPC program on all transports.
rpc_call(): Remote calls the specified procedure on the specified remote host.
rpc_broadcast(): Broadcasts a call message across all transports of the specified type.
svc_run(): RPC library's remote procedure dispatcher.