|
|
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.
Assume we have the following header file:
/* * time_prot.h */The following code implements the client side of a trivial date service, written at the top level:#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)
#include <stdio.h> #include <rpc/rpc.h> #include "time_prot.h"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.#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); }
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.
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.
This is the code for the time server:
#include <stdio.h> #include <rpc/rpc.h> #include "time_prot.h"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.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); } }
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.
In this example, rslt is not declared as static because svc_sendreply is called from within the dispatch function.