RPC Programming: 1.0 Prerequisites
RPC Programming: 1.0 Prerequisites
RPC Programming: 1.0 Prerequisites
1.0 Prerequisites
• Some knowledge of C & Unix
• A network with at least two connected machines.
2.0 Objectives:
1. To understand the basic principles of RPC
2. To develop a program which returns the current date and time on a remote machine.
3. To further develop this program so it may call the server if available, on any remote
machine.
3.0 Introduction
This worksheet is an introduction to programming using Remote Procedure Calls, (RPC).
RPC particularly when using the protocol compiler rpcgen, is an easy way to begin net-
work programming. Whilst very sophisticated services may be created using RPC (for
example the NFS (network file system) service, to port a small program to use RPC to run
part of itself on a remote machine is usually straightforward.
Lets consider a simple program than obtains, formats and prints the date & time:
main()
{
char *p;
printf(“%s”, p);
If you are familiar with C programming, you may notice that the data is stored in a static
variable in getdatestr(). When we come to using RPC, variables we wish to communicate
between computers will be REQUIRED to be static.
This simple program works on the local machine. If we want to execute part on one
machine and obtain the result back, we can use RPC
Client Server
main()
{
cp ====================================== getdatestr()
printf(“%s”,cp);
program REMTIMEPROG
{
Firstly, we have defined a type and a constant. After we have compiled the protocol, these
will be available for use to use in our own code.
Next, we defined our program (server), version, and procedure numbers. These are all long
ints and are used to register the service with the portmapper. Whilst version and procedure
numbers are a free choice, program numbers must be chosen with care. Predefined ser-
vices are listed in /etc/rpc, with protocol definitions for standard services in the directory /
usr/include/rpcsvc. Have a look at the definition for nfs, how many procedures are avail-
able?
TABLE 1. Program Number Ranges
So for our service, we need to choose a number from 0x20000000. The example uses
0x20650609. Once the protocol has been compiled we can refer to this as REMTIME-
PROG. Version numbers are to allow incompatible services to co-exist. If you are develop-
ing a service which evolves over time, different versions should have different version
numbers. Procedure numbers are how the client & server keep track of what procedure
should be called. Each function requires a unique number. Naming conventions recom-
mend the procedure name to be in uppercase (in this case GETDATESTR), and the proto-
col compiler will generate stubs with the procedure number added; for the client to call
getdatestr_1(), and for the server to provide getdate_1_svc().
rpcgen remtime.x
After compilation rpcgen will have generated 4 files. For our example these are:
1. remtime_xdr.c
eXternal Data Representation code. This should be linked into both client & server if
needed. It provides the code to convert data structures into a neutral format for transmis-
sion, and then back to the native format for the machine. This is to reduce the problems
you would get were say, one of your machines big-endian, and the other little-endian.
2. remtime_clnt.c
Client stub code. This handles interfacing with the XDR routines and provides the getdat-
estr() procedure ready for our client.
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
datestr *
getdatestr_1(void *argp, CLIENT *clnt)
{
static datestr clnt_res;
3. remtime_svc.c
The server stubs. This is essentially the complete server without the code to perform the
services we wish to offer. As a result, it is more complicated than the client stub. I’ve
included it here for information. It’s useful to understand what it does, but you can just
treat it as a blackbox when programming at this level.
/*
* Please do not edit this file.
* It was generated using rpcgen.
#include “remtime.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
remtimeprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
union {
int fill;
} 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 GETDATESTR:
_xdr_argument = (xdrproc_t) xdr_void;
_xdr_result = (xdrproc_t) xdr_datestr;
local = (char *(*)(char *, struct svc_req *))
getdatestr_1_svc;
break;
default:
svcerr_noproc (transp);
return;
}
memset ((char *)&argument, 0, sizeof (argument));
if (!svc_getargs (transp, _xdr_argument, (caddr_t) &argument)) {
svcerr_decode (transp);
return;
}
result = (*local)((char *)&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, _xdr_result, result))
{
svcerr_systemerr (transp);
}
if (!svc_freeargs (transp, _xdr_argument, (caddr_t) &argument)) {
fprintf (stderr, “unable to free arguments”);
int
main (int argc, char **argv)
{
register SVCXPRT *transp;
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
fprintf (stderr, “cannot create udp service.”);
exit(1);
}
if (!svc_register(transp, REMTIMEPROG, REMTIMEVERS, remtimeprog_1,
IPPROTO_UDP)) {
fprintf (stderr, “unable to register (REMTIMEPROG, REMTI-
MEVERS, udp).”);
exit(1);
}
svc_run ();
fprintf (stderr, “svc_run returned”);
exit (1);
/* NOTREACHED */
}
4. remtime.h
This is the header file to be included in the client & server. It includes prototypes for the
services we have defined, and any constants, typedefs etc. It contains code for C++, ANSI
C and K&R C. Only the ANSI C definitions are included here.
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#ifndef _REMTIME_H_RPCGEN
#include <rpc/rpc.h>
#define MAXSTRLEN 80
#endif /* !_REMTIME_H_RPCGEN */
#include <stdio.h>
#include <rpc/rpc.h>
#include “remtime.h”
int main(void)
{
CLIENT *client; /* client handle - required */
datestr *resstring; /* pointer to a datestr to hold
result */
We can now compile our client. We require to compile and link the file above (remclient.c)
which we’ve written, with the remtime_clnt.c and remtime_xdr.c produced by rpcgen.
/* #include <stdio.h> */
#include <rpc/rpc.h>
#include <time.h>
#include “remtime.h”
secs = time(NULL);
strcpy(buf,ctime(&secs));
cp = buf;
dp = &cp;
return(dp);
}
For our server, the above code when linked with remtime_svc.c and remtime_xdr.c pro-
vides a complete server. All we need to do is to define the functionality we wish to named
8.0 To do.
1. Type in the protocol specification, compile it, build the client & server and test on local
machine.
2. Modify the client source so if the remote procedure returns a null pointer (the error
condition), clnt_perror is called to explain the error.
3. Modify the client source so that instead of running against localhost, client connects to
another machine.
4. Modify the client so that the server can be specified on the command line.
9.1 Books
Bloomer, John “Power Programming with RPC”, O’Reilly & Associates, 1992.
Stevens, W. Richard, “Unix Network Programming”, Prentice Hall, 1990, chapter 18.
Peterson, L.L. & Davie, Bruce S., “Computer Networks - A Systems Approach”, Morgan
Kaufmann, 2000.
9.2 URL’s
http://uw7doc.sco.com/SDK_netapi/CTOC-rpcpN.intro.html, Programming with RPC
from SCO Unix.
http://advm2.gm.fh-koeln.de/doc_link/en_US/a_doc_lib/aixprggd/progcomc/toc.htm.
AIX programming manual - see chapter 8.