Programming with Remote Procedure Calls (RPC)

The top level

At the top level, the application can specify the type of transport that it wants to use, but not an individual transport. This level differs from the simplified interface to RPC in that the application is responsible for creating its own transport handles, on both the client and server sides.

Top level: client side

Assume we have the following header file:

    * time_prot.h

#include <rpc/types.h>

struct timev { int second; int minute; int hour; }; typedef struct timev timev;

bool_t xdr_timev(xdrsp, resp) XDR *xdrsp; struct timev *resp; { if (!xdr_int(xdrsp, &resp->second)) return (FALSE); if (!xdr_int(xdrsp, &resp->minute)) return (FALSE); if (!xdr_int(xdrsp, &resp->hour)) return (FALSE); return (TRUE); }

#define TIME_PROG ((u_long)76) #define TIME_VERS ((u_long)1) #define TIME_GET ((u_long)1)

The following code implements the client side of a trivial date service, written at the top level:
   #include <stdio.h>
   #include <rpc/rpc.h>
   #include "time_prot.h"

#define TOTAL (30)

/* * Caller of trivial date service * usage: calltime hostname */ main(argc,argv) int argc; char *argv[]; { struct timeval timeout; CLIENT *client; enum clnt_stat stat; struct timev timev; char *nettype;

if (argc != 2 && argc != 3) { fprintf(stderr,"usage: %s host [nettype]\n",argv[0]); }

if (argc == 2) nettype = "netpath"; /* Default */ else nettype = argv[2]; client = clnt_create(argv[1], TIME_PROG, TIME_VERS, nettype); if (client == NULL) { clnt_pcreateerror("Couldn't create client"); exit(1); } timeout.tv_sec = TOTAL; timeout.tv_usec = 0; stat = clnt_call(client, TIME_GET, xdr_void, NULL, xdr_timev, &timev, timeout); if (stat != RPC_SUCCESS) { clnt_perror(client, "Call failed"); exit(1); } printf("%s: %02d:%02d:%02d GMT\n", nettype, timev.hour, timev.minute, timev.second); exit(0); }

Note that, when this program is run, if nettype is not given on the command line, the code assigns it to point to the string ``"netpath"''. Whenever the routines in the RPC libraries encounter this string, they consult the NETPATH environment variable for the user's list of acceptable network identifiers.

If the client handle cannot be created, the reason for the failure can be printed using clnt_pcreateerror, or the error status can be obtained via the global variable rpc_createerr.

NOTE: In applications linked with the Threads Library, distinct instances of rpc_createerr and errno will be supported for each thread. The value of rpc_createerr for the current thread will be returned by the new function get_rpc_createerr(NS). You may set the value of rpc_createerr by calling the function set_rpc_createerr, which is also described on the get_rpc_createerr(NS) manual page. The symbol rpc_createerr will continue to be available in read-only form, but will generate a compile-time error if you try to assign it a value directly. You may continue to assign values to errno directly, however. See ``Multithreaded network programming'' for more information about using RPC with the Threads Library.

After the client handle is created, clnt_call is used to make the remote call. It takes as arguments the remote procedure number, an XDR filter for the input argument and the argument pointer, an XDR filter for the result and the result pointer, and the time-out period of the call. Normally, this last should not be 0. In this particular example there are no arguments, and thus xdr_void has been specified.

Top level: server side

This is the code for the time server:

   #include <stdio.h>
   #include <rpc/rpc.h>
   #include "time_prot.h"

static void time_prog();

main(argc,argv) int argc; char *argv[]; { int transpnum; char *nettype;

if (argc == 2) nettype = argv[1]; else nettype = "netpath"; /* Default */ transpnum = svc_create(time_prog, TIME_PROG, TIME_VERS, nettype); if (transpnum == 0) { fprintf(stderr,"%s: cannot create %s service.\n", argv[0], nettype); exit(1); } svc_run(); }

/* * The server dispatch function */ static void time_prog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { struct timev rslt; long thetime;

switch (rqstp->rq_proc) { case NULLPROC: svc_sendreply(transp, xdr_void, NULL); return;

case TIME_GET: break;

default: svcerr_noproc(transp); return; }

thetime = time(0); rslt.second = thetime % 60; thetime /= 60; rslt.minute = thetime % 60; thetime /= 60; rslt.hour = thetime % 24; if (! svc_sendreply(transp, xdr_timev, &rslt)) { svcerr_systemerr(transp); } }

svc_create returns the number of transports on which it could create server handles. time_prog is the dispatch function called by svc_run whenever there is a request for its given program and version number.

Here the remote procedure takes no arguments. Had arguments been required,

   svc_getargs(transport, XDR_filter, argument_pointer)
could have been used to deserialize (XDR decode) the arguments. In such cases, svc_freeargs should be used to free up the arguments after the actual call has been made. The server reply results are sent back to the client using svc_sendreply.

It is recommended that rpcgen be used to generate the dispatch function which can later be customized.

NOTE: When rpcgen is used to generate the dispatch function, svc_sendreply is called only after the actual procedure has returned, and hence it is essential to have rslt (in this example) declared as ``static'' within that actual procedure.

In this example, rslt is not declared as static because svc_sendreply is called from within the dispatch function.

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005