Your IP : 216.73.216.40


Current Path : /var/www/html/bibhas.ghoshal/lecture_slides/distributed/
Upload File :
Current File : /var/www/html/bibhas.ghoshal/lecture_slides/distributed/Remote Procedure Calls (RPC).html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<!--Converted with LaTeX2HTML 97.1 (release) (July 13th, 1997)
 by Nikos Drakos (nikos@cbl.leeds.ac.uk), CBLU, University of Leeds
* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan
* with significant contributions from:
  Jens Lippman, Marek Rouchal, Martin Wilck and others -->
<html><head>
<title>Remote Procedure Calls (RPC)</title>
<meta name="description" content="Remote Procedure Calls (RPC)">
<meta name="keywords" content="C">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="STYLESHEET" href="Remote%20Procedure%20Calls%20%28RPC%29_files/C.css">
</head>
<body>
<!--Table of Child-Links-->
<a name="CHILD_LINKS"><strong>Subsections</strong></a>
<ul>
<li><a name="tex2html1441" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003310000000000000000">
What Is RPC</a>
</li><li><a name="tex2html1442" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003320000000000000000">
How RPC Works</a>
</li><li><a name="tex2html1443" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003330000000000000000">
RPC Application Development</a>
<ul>
<li><a name="tex2html1444" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003331000000000000000">
Defining the Protocol</a>
</li><li><a name="tex2html1445" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003332000000000000000">
Defining Client and Server Application Code</a>
</li><li><a name="tex2html1446" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003333000000000000000">
Compliling and running the application</a>
</li></ul>
</li><li><a name="tex2html1447" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003340000000000000000">
Overview of Interface Routines</a>
<ul>
<li><a name="tex2html1448" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003341000000000000000">
Simplified Level
Routine Function</a>
</li><li><a name="tex2html1449" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003342000000000000000">
Top Level
Routines</a>
</li></ul>
</li><li><a name="tex2html1450" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003350000000000000000">
Intermediate Level Routines</a>
<ul>
<li><a name="tex2html1451" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003351000000000000000">
Expert Level Routines</a>
</li><li><a name="tex2html1452" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003352000000000000000">
Bottom Level Routines</a>
</li></ul>
</li><li><a name="tex2html1453" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003360000000000000000">
The Programmer's Interface to RPC</a>
<ul>
<li><a name="tex2html1454" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003361000000000000000">
Simplified Interface</a>
</li><li><a name="tex2html1455" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003362000000000000000">
Passing Arbitrary Data Types</a>
</li><li><a name="tex2html1456" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003363000000000000000">
Developing High Level RPC Applications</a>
<ul>
<li><a name="tex2html1457" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003363100000000000000">
Defining the protocol</a>
</li></ul>
</li><li><a name="tex2html1458" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003364000000000000000">
Sharing the data</a>
<ul>
<li><a name="tex2html1459" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003364100000000000000">
The Server Side</a>
</li><li><a name="tex2html1460" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003364200000000000000">
The Client Side</a>
</li></ul>
</li></ul>
</li><li><a name="tex2html1461" href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#SECTION003370000000000000000">
Exercise</a>
</li></ul>
<!--End of Table of Child-Links-->
<hr>
<h1><a name="SECTION003300000000000000000">
Remote Procedure Calls (RPC)</a>
</h1>
<p>
This chapter provides an overview of Remote Procedure Calls (RPC) RPC.
</p><p></p><h1><a name="SECTION003310000000000000000">
What Is RPC</a>
</h1>
<p>
RPC is a powerful technique for constructing distributed, client-server
based applications. It is based on extending the notion of conventional,
or local procedure calling, so that the called procedure need not exist
in the same address space as the calling procedure. The two processes may
be on the same system, or they may be on different systems with a network
connecting them. By using RPC, programmers of distributed applications
avoid the details of the interface with the network. The transport
independence of RPC isolates the application from the physical and
logical elements of the data communications mechanism and allows the
application to use a variety of transports.
</p><p>
RPC makes the client/server model of computing more powerful and easier to
program. When combined with the ONC RPCGEN protocol compiler
(Chapter&nbsp;<a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node34.html#ch:rpcgen">33</a>) clients transparently make remote calls through a
local procedure interface.
</p><p></p><h1><a name="SECTION003320000000000000000">
How RPC Works</a>
</h1>
<p>
An RPC is analogous to a function call. Like a
function call, when an RPC is made, the calling arguments are passed to
the remote procedure and the caller waits for a response to be returned
from the remote procedure. Figure&nbsp;<a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#fig:rpc">32.1</a> shows the flow of activity
that takes place during an RPC call between two networked systems. The
client makes a procedure call that sends a request to the server and
waits. The thread is blocked from processing until either a reply is
received, or it times out. When the request arrives, the server calls a
dispatch routine that performs the requested service, and sends the reply
to the client. After the RPC call is completed, the client program
continues. RPC specifically supports network applications.
</p><p><a name="fig:rpc">&nbsp;</a><img src="Remote%20Procedure%20Calls%20%28RPC%29_files/rpc.gif">
</p><p>
<b>Fig.&nbsp;<a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node33.html#fig:rpc">32.1</a> Remote Procedure Calling Mechanism</b>
A remote procedure is uniquely identified by the triple:
(program number, version number, procedure number)
The program number identifies a group of related remote procedures, each of which has a unique
procedure number.
A program may consist of one or more versions. Each version consists of a collection of procedures which
are available to be called remotely. Version numbers enable multiple versions of an RPC protocol to be
available simultaneously.
Each version contains a a number of procedures that can be called remotely. Each procedure has a
procedure number.
</p><p></p><h1><a name="SECTION003330000000000000000">
RPC Application Development</a>
</h1>
<p>
Consider an example:
</p><p>
A client/server lookup in a personal database on a remote machine. Assuming that
we cannot access the database from the local machine (via NFS).
</p><p>
We use UNIX to run a remote shell and execute the command this way. There are
some problems with this method:
</p><p></p><ul>
<li> the command may be slow to execute.
</li><li> You require an login account on the remote machine.
</li></ul>
<p>
The RPC alternative is to
</p><p></p><ul>
<li> establish an server on the remote machine that can repond to queries.
</li><li> Retrieve information by calling a query which will be quicker than
previous approach.
</li></ul>
<p>
To develop an RPC application the following steps are needed:
</p><p></p><ul>
<li> Specify the protocol for client server communication
</li><li> Develop the client program
</li><li> Develop the server program
</li></ul>
<p>
The programs will be compiled seperately. The communication protocol is achieved
by generated stubs and these stubs and rpc (and other libraries) will need to be
linked in.
</p><p></p><h2><a name="SECTION003331000000000000000">
Defining the Protocol</a>
</h2>
<p>
The easiest way to define and generate the protocol is to use a protocol
complier such as <tt>rpcgen</tt> which we discuss is Chapter&nbsp;<a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node34.html#ch:rpcgen">33</a>.
</p><p>
For the protocol you must identify the name of the service procedures, and data
types of parameters and return arguments.
</p><p>
The protocol compiler reads a definitio and automatically generates client and
server stubs.
</p><p><tt>rpcgen</tt> uses its own language (RPC language or RPCL) which looks very
similar to preprocessor directives.
</p><p><tt>rpcgen</tt>  exists as a standalone executable compiler that reads special
files denoted by a <tt>.x</tt> prefix.
</p><p>
So to compile a RPCL file you simply do
</p><p><tt>rpcgen rpcprog.x</tt>
</p><p>
This will generate possibly four files:
</p><p></p><ul>
<li> <tt>rpcprog_clnt.c</tt> -- the client stub
</li><li> <tt>rpcprog_svc.c</tt> -- the server stub
</li><li> <tt>rpcprog_xdr.c</tt> -- If necessary XDR (external data representation)
filters
</li><li> <tt>rpcprog.h</tt> -- the header file needed for any XDR filters.
</li></ul>
<p>
The external data representation (XDR) is an data abstraction needed for machine
independent communication. The client and server need not be machines of the
same type.
</p><p></p><h2><a name="SECTION003332000000000000000">
Defining Client and Server Application Code</a>
</h2>
<p>
We must now write the the client and application code. They must communicate via
procedures and data types specified in the Protocol.
</p><p>
The service side will have to register the procedures that may be called by the
client and receive and return any data required for processing.
</p><p>
The client
application call the remote procedure pass  any required data and will receive
the retruned data.
</p><p>
There are several levels of application interfaces that may be used to develop
RPC applications. We will briefly disuss these below before exapnading thhe most
common of these in later chapters.
</p><p></p><h2><a name="SECTION003333000000000000000">
Compliling and running the application</a>
</h2>
<p>
Let us consider the full compilation model required to run a RPC application.
Makefiles are useful for easing the burden of compiling RPC applications but it
is necessary to understand the complete model before one can assemble a
convenient makefile.
</p><p>
Assume the the client program is called <tt>rpcprog.c</tt>, the service program
is <tt>rpcsvc.c</tt> and that the protocol has been defined in <tt>rpcprog.x</tt> and that
<tt>rpcgen </tt> has been used to produce the stub and filter files: <tt>
rpcprog_clnt.c, rpcprog_svc.c, rpcprog_xdr.c, rpcprog.h</tt>.
</p><p>
The client and server program must include (<code>#include "rpcprog.h"</code>
</p><p>
You must then:
</p><p></p><ul>
<li> compile the client code:
<p></p><pre>cc -c  rpcprog.c
</pre>
</li><li> compile the client stub:
<p></p><pre>cc -c  rpcprog_clnt.c
</pre>
</li><li> compile the XDR filter:
<p></p><pre>cc -c  rpcprog_xdr.c
</pre>
</li><li> build the client executable:
<p></p><pre>cc -o rpcprog rpcprog.o rpcprog_clnt.o rpcprog_xdr.c
</pre>
</li><li> compile the service procedures:
<p></p><pre>cc -c  rpcsvc.c
</pre>
</li><li> compile the server stub:
<p></p><pre>cc -c  rpcprog_svc.c
</pre>
</li><li> build the server executable:
<p></p><pre>cc -o rpcsvc rpcsvc.o rpcprog_svc.o rpcprog_xdr.c
</pre>
</li></ul>
<p>
Now simply run the programs <tt>rpcprog</tt> and <tt>rpcsvc</tt> on the client and
server respectively. The server procedures must be registered before the client
can call them.
</p><p></p><h1><a name="SECTION003340000000000000000">
Overview of Interface Routines</a>
</h1>
<p>
RPC has multiple levels of application interface to its services. These levels provide different degrees of
control balanced with different amounts of interface code to implement. In order of increasing control and
complexity. This section gives a summary of the routines available at each level.
Simplified Interface Routines
</p><p>
The simplified interfaces are used to make remote procedure calls to routines on other machines, and
specify only the type of transport to use. The routines at this level are used for most applications.
Descriptions and code samples can be found in the section, Simplified Interface @ 3-2.
</p><p></p><h2><a name="SECTION003341000000000000000">
Simplified Level
Routine Function</a>
</h2>
<p><tt>rpc_reg()</tt> -- Registers a procedure as an RPC program on all
transports of the specified type.
</p><p><tt>rpc_call()</tt> -- Remote calls the specified
procedure on the specified remote host.
</p><p><tt>rpc_broadcast()</tt> -- Broadcasts a call
message across all transports of the specified type. Standard Interface
Routines The standard interfaces are divided into top level, intermediate
level, expert level, and bottom level. These interfaces give a developer
much greater control over communication parameters such as the transport
being used, how long to wait beforeresponding to errors and
retransmitting requests, and so on.
</p><p></p><h2><a name="SECTION003342000000000000000">
Top Level
Routines</a>
</h2>
<p>
At the top level, the interface is still simple, but the program
has to create a client handle before making a call or create a server
handle before receiving calls. If you want the application to run on all
transports, use this interface. Use of these routines and code samples
can be found in Top Level Interface
</p><p><tt>clnt_create()</tt> -- Generic client creation. The program
tells <tt>clnt_create()</tt> where the server is located and the type of
transport to use.
</p><p><tt>clnt_create_timed()</tt> Similar to <tt>clnt_create()</tt> but
lets the programmer specify the maximum time allowed for each type of
transport tried during the creation attempt.
</p><p><tt>svc_create()</tt> -- Creates server
handles for all transports of the specified type. The program tells
<tt>svc_create()</tt> which dispatch function to use.
</p><p><tt>clnt_call()</tt> -- Client calls a
procedure to send a request to the server.
</p><p></p><h1><a name="SECTION003350000000000000000">
Intermediate Level Routines</a>
</h1>
<p>
The intermediate level interface of RPC lets you control details.
Programs written at these lower levels are more complicated but run more
efficiently. The intermediate level enables you to specify the transport
to use.
</p><p><tt>clnt_tp_create()</tt> -- Creates a client handle for the
specified transport.
</p><p><tt>clnt_tp_create_timed()</tt> -- Similar to <tt>clnt_tp_create()</tt>
but lets the programmer specify the maximum time allowed.
<tt>svc_tp_create()</tt> Creates a server handle for the specified transport.
</p><p><tt>clnt_call()</tt> -- Client calls a procedure to send a request to the
server.
</p><p></p><h2><a name="SECTION003351000000000000000">
Expert Level Routines</a>
</h2>
<p>
The expert level contains a larger set of routines with which to specify transport-related parameters. Use
of these routines
</p><p><tt>clnt_tli_create()</tt> -- Creates a client handle for the specified
transport.
</p><p><tt>svc_tli_create()</tt> -- Creates a server handle for the specified
transport.
</p><p><tt>rpcb_set()</tt> -- Calls rpcbind to set a map between an RPC service
and a network address.
</p><p><tt>rpcb_unset() </tt> -- Deletes a mapping set by <tt>rpcb_set()</tt>.
</p><p><tt>rpcb_getaddr()</tt> -- Calls rpcbind to get the transport addresses of
specified RPC services.
</p><p><tt>svc_reg()</tt> -- Associates the specified program and
version number pair with the specified dispatch routine.
</p><p><tt>svc_unreg()</tt> -- Deletes an association set by <tt>svc_reg()</tt>.
</p><p><tt>clnt_call()</tt> -- Client calls a procedure to send a request to the
server.
</p><p></p><h2><a name="SECTION003352000000000000000">
Bottom Level Routines</a>
</h2>
<p>
The bottom level contains routines used for full control of transport
options.
</p><p><tt>clnt_dg_create()</tt> -- Creates an RPC client handle for the
specified remote program, using a connectionless transport.
</p><p><tt>svc_dg_create()</tt> -- Creates an RPC server handle, using a
connectionless transport.
</p><p><tt>clnt_vc_create()</tt> -- Creates an RPC client handle for
the specified remote program, using a connection-oriented transport.
</p><p><tt>svc_vc_create()</tt> -- Creates an RPC server handle, using a
connection-oriented transport.
</p><p><tt>clnt_call()</tt> --  Client calls a procedure to
send a request to the server.
</p><p></p><h1><a name="SECTION003360000000000000000">
The Programmer's Interface to RPC</a>
</h1>
<p>
This section addresses the C interface to RPC and describes how to write
network applications using RPC. For a complete specification of the
routines in the RPC library, see the <tt>rpc</tt> and related <tt>man</tt>
pages.
</p><p></p><h2><a name="SECTION003361000000000000000">
Simplified Interface</a>
</h2>
<p>
The simplified interface is the easiest level to use because it does not
require the use of any other RPC routines. It also limits control of the
underlying communications mechanisms. Program development at this level
can be rapid, and is directly supported by the <tt>rpcgen</tt> compiler. For
most applications, rpcgen and its facilities are sufficient. Some RPC
services are not available as C functions, but they are available as RPC
programs. The simplified interface library routines provide direct access
to the RPC facilities for programs that do not require fine levels of
control.
</p><p>
Routines such as <tt>rusers</tt> are in the RPC services library
<tt>librpcsvc</tt>. <tt>rusers.c</tt>, below,  is a program that displays the
number of users on a remote host. It calls the RPC library routine,
<tt>rusers</tt>.
</p><p>
The <tt>program.c</tt> program listing:
</p><p></p><pre>#include &lt;rpc/rpc.h&gt; 
#include &lt;rpcsvc/rusers.h&gt;
#include &lt;stdio.h&gt;

/*
* a program that calls the
* rusers() service
*/

main(int argc,char **argv)

{
int num;
if (argc != 2) {
   fprintf(stderr, "usage: %s hostname\n",
   argv[0]);
   exit(1);
   }

if ((num = rnusers(argv[1])) &lt; 0) {
   fprintf(stderr, "error: rusers\n");
   exit(1);
  }

fprintf(stderr, "%d users on %s\n", num, argv[1] );
exit(0);
}
</pre>
<p>
Compile the program with:
</p><p></p><pre>cc program.c -lrpcsvc -lnsl
</pre>
<p><b>The Client Side</b>
</p><p>
There is just one function on the client side of the simplified interface <tt>
rpc_call()</tt>.
</p><p>
It has nine parameters:
</p><p></p><pre>int 
rpc_call (char *host /* Name of server host */,
    u_long prognum /* Server program number */,
    u_long versnum /* Server version number */,
    xdrproc_t inproc /* XDR filter to encode arg */,
    char *in /* Pointer to argument */,
    xdr_proc_t outproc /* Filter to decode result */,
    char *out /* Address to store result */,
    char *nettype /* For transport selection */);
</pre>
<p>
This function calls the procedure specified by <tt>prognum, versum,</tt> and
<tt>procnum</tt> on the host. The argument  to be passed
to the remote procedure is pointed to by the <tt>in</tt> parameter, and <tt>
inproc</tt> is the XDR filter to encode this argument. The <tt>out</tt> parameter is an
address where the result from the remote procedure is to be placed. <tt>outproc</tt>
is an XDR filter which will decode the result and place it at this address.
</p><p>
The
client blocks on <tt>rpc_call()</tt> until it receives a reply from the server. If
the server accepts, it returns <tt>RPC_SUCCESS</tt> with the value of zero. It will
return a non-zero value if the call was unsuccessful. This value can be cast to
the type <tt>clnt_stat</tt>, an enumerated type defined in the RPC include files
(&lt;rpc/rpc.h&gt;) and interpreted by the <tt>clnt_sperrno()</tt> function. This
function returns a pointer to a standard RPC error message corresponding to the
error code. In the example, all "visible" transports listed in <tt>
/etc/netconfig</tt> are tried. Adjusting the number of retries requires use of the
lower levels of the RPC library. Multiple arguments and results are handled by
collecting them in structures.
</p><p>
The example  changed to
use the simplified interface, looks like
</p><pre>#include &lt;stdio.h&gt;
#include &lt;utmp.h&gt; 
#include &lt;rpc/rpc.h&gt;
#include &lt;rpcsvc/rusers.h&gt;

/* a program that calls the RUSERSPROG
* RPC program
*/

main(int argc, char **argv)

{
   unsigned long nusers;
   enum clnt_stat cs;
   if (argc != 2) {
     fprintf(stderr, "usage: rusers hostname\n");
     exit(1);
    }

   if( cs = rpc_call(argv[1], RUSERSPROG,
          RUSERSVERS, RUSERSPROC_NUM, xdr_void,
          (char *)0, xdr_u_long, (char *)&amp;nusers,
          "visible") != RPC_SUCCESS ) {
              clnt_perrno(cs);
              exit(1);
            }

   fprintf(stderr, "%d users on %s\n", nusers, argv[1] );
   exit(0);
}
</pre>
<p>
Since data types may be represented differently on different machines,
<tt>rpc_call()</tt> needs both the type of, and a pointer to, the RPC argument
(similarly for the result). For <tt>RUSERSPROC_NUM</tt>, the return value is an
unsigned long, so the first return parameter of <tt>rpc_call()</tt> is
<tt>xdr_u_long</tt> (which is for an unsigned long) and the second is
<code>&amp;nusers</code> (which points to unsigned long storage). Because
<tt>RUSERSPROC_NUM</tt> has no argument, the XDR encoding function of <tt>
rpc_call()</tt> is <tt>xdr_void()</tt> and its argument is <tt>NULL</tt>.
</p><p><b>The Server Side</b>
</p><p>
The server program using the
simplified interface is very straightforward. It simply calls <tt>rpc_reg()</tt> to
register the procedure to be called, and then it calls <tt>svc_run()</tt>, the RPC
library's remote procedure dispatcher, to wait for requests to come in.
</p><p><tt>rpc_reg()</tt>
has the following prototype:
</p><p></p><pre>rpc_reg(u_long prognum /* Server program number */,
        u_long versnum /* Server version number */,
        u_long procnum /* server procedure number */,
        char *procname /* Name of remote function */,
        xdrproc_t inproc /* Filter to encode arg */,
        xdrproc_t outproc /* Filter to decode result */,
        char *nettype /* For transport selection */);
</pre>
<p><tt>svc_run()</tt> invokes service procedures in response to RPC call messages. The
dispatcher in <tt>rpc_reg()</tt> takes care of decoding remote procedure arguments
and encoding results, using the XDR filters specified when the remote procedure
was registered. Some notes about the server program:
</p><p></p><ul>
<li> Most RPC applications follow
the naming convention of appending a <tt>_1</tt> to the function name. The sequence
<tt>_n</tt> is added to the procedure names to indicate the version number <tt>n</tt>
of the service.
</li><li>
The argument and result are passed as addresses. This is true for all functions
that are called remotely. If you pass <tt>NULL</tt> as a result of a function, then
no reply is sent to the client. It is assumed that there is no reply to send.
</li><li>
The result must exist in static data space because its value is accessed after
the actual procedure has exited. The RPC library function that builds the RPC
reply message accesses the result and sends the value back to the client.
</li><li> Only
a single argument is allowed. If there are multiple elements of data, they should
be wrapped inside a structure which can then be passed as a single entity.
</li><li> The
procedure is registered for each transport of the specified type. If the type
parameter is <tt>(char *)NULL</tt>, the procedure is registered for all transports
specified in <tt>NETPATH</tt>. 
</li></ul>
<p>
You can sometimes
implement faster or more compact code than can <tt>rpcgen</tt>. <tt>rpcgen</tt> handles the
generic code-generation cases. The following program is an example of a
hand-coded registration routine.
It registers a
single procedure and enters <tt>svc_run()</tt> to service requests.
</p><p></p><pre>#include &lt;stdio.h&gt; 
#include &lt;rpc/rpc.h&gt;
#include &lt;rpcsvc/rusers.h&gt;

void *rusers();

main()
{
  if(rpc_reg(RUSERSPROG, RUSERSVERS,
        RUSERSPROC_NUM, rusers,
        xdr_void, xdr_u_long,
        "visible") == -1) {
           fprintf(stderr, "Couldn't Register\n");
            exit(1);
          }
   svc_run(); /* Never returns */
  fprintf(stderr, "Error: svc_run returned!\n");
  exit(1);
}
</pre>
<p><tt>rpc_reg()</tt> can be called as many times as is needed to register different
programs, versions, and procedures.
</p><p></p><h2><a name="SECTION003362000000000000000">
Passing Arbitrary Data Types</a>
</h2>
<p>
Data types passed to and received from remote procedures can be any of a set of predefined
types, or can be programmer-defined types. RPC handles arbitrary data structures,
regardless of different machines' byte orders or structure layout conventions, by always
converting them to a standard transfer format called external data representation (XDR)
before sending them over the transport. The conversion from a machine representation to XDR
is called serializing, and the reverse process is called deserializing. The translator
arguments of <tt>rpc_call()</tt> and <tt>rpc_reg()</tt> can specify an XDR primitive procedure,
like <tt>xdr_u_long()</tt>, or a programmer-supplied routine that processes a complete
argument structure. Argument processing routines must take only two arguments: a pointer to
the result and a pointer to the XDR handle.
</p><p>
The following XDR Primitive Routines are available:
</p><pre>xdr_int() xdr_netobj() xdr_u_long() xdr_enum()
xdr_long() xdr_float() xdr_u_int() xdr_bool()
xdr_short() xdr_double() xdr_u_short() xdr_wrapstring()
xdr_char() xdr_quadruple() xdr_u_char() xdr_void()
</pre>
<p>
The nonprimitive <tt>xdr_string()</tt>, which takes more than two parameters, is called from
<tt>xdr_wrapstring()</tt>.
</p><p>
For an example of a programmer-supplied routine, the structure:
</p><pre>struct simple {
   int a;
   short b;
  } simple;
</pre>
<p>
contains the calling arguments of a procedure. The XDR routine <tt>xdr_simple()</tt>
translates the argument structure as shown below:
</p><p></p><pre>#include &lt;rpc/rpc.h&gt;
#include "simple.h"

bool_t xdr_simple(XDR *xdrsp, struct simple *simplep)

{
   if (!xdr_int(xdrsp, &amp;simplep-&gt;a))
      return (FALSE);
   if (!xdr_short(xdrsp, &amp;simplep-&gt;b))
      return (FALSE);
   return (TRUE);
}
</pre>
<p>
An equivalent routine can be generated automatically by <tt>rpcgen</tt> (See
Chapter&nbsp;<a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node34.html#ch:rpcgen">33</a>).
</p><p>
An XDR routine returns nonzero (a C TRUE) if it completes
successfully, and zero otherwise.
</p><p>
For more complex data structures use the XDR prefabricated routines:
</p><p></p><pre>xdr_array() xdr_bytes() xdr_reference()
xdr_vector() xdr_union() xdr_pointer()
xdr_string() xdr_opaque()
</pre>
<p>
For example, to send a variable-sized array of integers, 
it is packaged in a structure containing the array
and its length:
</p><p></p><pre>struct varintarr {
int *data;
int arrlnth;
} arr;
</pre>
<p>
Translate the array with <tt>xdr_array()</tt>, as shown below:
</p><p></p><pre>bool_t xdr_varintarr(XDR *xdrsp, struct varintarr *arrp)

{
    return(xdr_array(xdrsp, (caddr_t)&amp;arrp-&gt;data, 
             (u_int *)&amp;arrp-&gt;arrlnth, MAXLEN, sizeof(int), xdr_int));
}
</pre>

The arguments of <tt> xdr_array()</tt> are the XDR handle, a pointer to the array,
a pointer to
the size of the array, the maximum array size, the size of each array element, and a
pointer to the XDR routine to translate each array element. 

If the size of the array is
known in advance, use <tt> xdr_vector()</tt> instread as is more efficient:

<pre>int intarr[SIZE];

bool_t xdr_intarr(XDR *xdrsp, int intarr[])
{
   return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int), xdr_int));
}
</pre>
<p>
XDR converts quantities to 4-byte multiples when serializing. For arrays of characters,
each character occupies 32 bits. <tt>xdr_bytes()</tt> packs characters. It has four parameters
similar to the first four parameters of <tt>xdr_array()</tt>.
</p><p>
Null-terminated strings are translated by <tt>xdr_string()</tt>. It is like <tt>xdr_bytes()</tt>
with no length parameter. On serializing it gets the string length from <tt>strlen()</tt>, and
on deserializing it creates a null-terminated string.
</p><p><tt>xdr_reference()</tt> calls
the built-in functions xdr_string() and <tt>xdr_reference()</tt>, which translates
pointers to pass a string, and struct simple from the previous examples. An
example use  of <tt>xdr_reference()</tt> is as follows:
</p><p></p><pre>struct finalexample {
    char *string;
    struct simple *simplep;
   } finalexample;

bool_t xdr_finalexample(XDR *xdrsp, struct finalexample *finalp)

{  if (!xdr_string(xdrsp, &amp;finalp-&gt;string, MAXSTRLEN))
        return (FALSE);
   if (!xdr_reference( xdrsp, &amp;finalp-&gt;simplep, sizeof(struct simple), xdr_simple))
       return (FALSE);
    return (TRUE);
}
</pre>
<p>
Note that<tt>xdr_simple()</tt> could have been called here instead of <tt>xdr_reference()</tt>
.
</p><p></p><h2><a name="SECTION003363000000000000000">
Developing High Level RPC Applications</a>
</h2>
<p>
Let us now introduce some further functions and see how we develop an application using high level RPC
routines. We will do this by studying an example.
</p><p>
We will develop a remote directory reading utility.
</p><p>
Let us first consider how we would write a local directory reader. We have seem how to do this already
in Chapter&nbsp;<a href="https://users.cs.cf.ac.uk/Dave.Marshall/C/node20.html#ch:file">19</a>.
</p><p>
Consider the program to consist of two files:
</p><p></p><ul>
<li> <tt>lls.c</tt> -- the main program which calls a routine in a local module <tt>read_dir.c</tt>
<p></p><pre>/*
 * ls.c: local directory listing main - before RPC
 */
#include &lt;stdio.h&gt;
#include &lt;strings.h&gt;
#include "rls.h"

main (int argc, char **argv)

{
        char    dir[DIR_SIZE];

        /* call the local procedure */
        strcpy(dir, argv[1]);	/* char dir[DIR_SIZE] is coming and going... */
        read_dir(dir);

        /* spew-out the results and bail out of here! */
        printf("%s\n", dir);

        exit(0);
}
</pre>
</li><li> <tt>read_dir.c</tt> -- the file containing the <em>local</em> routine <tt>read_dir()</tt>.
<p></p><pre>/* note - RPC compliant procedure calls take one input and
   return one output. Everything is passed by pointer.  Return
   values should point to static data, as it might have to 
   survive some while. */
#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/dir.h&gt;     /* use &lt;xpg2include/sys/dirent.h&gt; (SunOS4.1) or
        &lt;sys/dirent.h&gt; for X/Open Portability Guide, issue 2 conformance */
#include "rls.h"

read_dir(char    *dir)
   /* char dir[DIR_SIZE] */
{
        DIR * dirp;
        struct direct *d;
		      printf("beginning ");

        /* open directory */
        dirp = opendir(dir);
        if (dirp == NULL)
                return(NULL);

        /* stuff filenames into dir buffer */
        dir[0] = NULL;
        while (d = readdir(dirp))
                sprintf(dir, "%s%s\n", dir, d-&gt;d_name);

        /* return the result */
		  printf("returning ");
        closedir(dirp);
        return((int)dir);  /* this is the only new line from Example 4-3 */
}
</pre>
</li><li> the header file <tt>rls.h</tt> contains only the following (for now at least)
<p></p><pre>#define DIR_SIZE 8192
</pre>
<p>
Clearly we need to share the size between the files. Later when we develop RPC versions more
information will need to be added to this file.
</p><p></p></li></ul>
<p>
This local program would be compiled as follows:
</p><p></p><pre>cc lls.c read_dir.c -o lls
</pre>
<p>
Now we want to modify this program to work over a network: Allowing us to inspect directories of a
remote server accross a network.
</p><p>
The following steps will be required:
</p><p></p><ul>
<li> We will have to convert the <tt>read_dir.c</tt>, to run on the server.
<ul>
<li> We will have to register the server and the routine <tt>read_dir()</tt> on the server/.
</li></ul>
</li><li> The client <tt>lls.c</tt> will have to call the routine as a remote procedure.
</li><li> We will have to define the protocol for communication between the client and the server programs.
</li></ul><h3><a name="SECTION003363100000000000000">
Defining the protocol</a>
</h3>
<p>
We can can use simple <tt>NULL</tt>-terminated strings for passing and receivong the directory name and
directory contents. Furthermore, we can embed the passing of these parameters directly in the client
and server code.
</p><p>
We therefore need to specify the program, procedure and version numbers for client and servers. This
can be done automatically using <tt>rpcgen</tt> or relying on prdefined macros in the simlified
interface. Here we will specify them manually.
</p><p>
The server and client must agree <em>ahead of time</em> what logical adresses thney will use (The
physical addresses do not matter they are hidden from the application developer)
</p><p>
Program numbers are defined in a standard way:
</p><p></p><ul>
<li> 0<i>x</i>00000000 - 0<i>x</i>1<i>FFFFFFF</i>: Defined by Sun
</li><li> 0<i>x</i>20000000 - 0<i>x</i>3<i>FFFFFFF</i>: User Defined
</li><li> 0<i>x</i>40000000 - 0<i>x</i>5<i>FFFFFFF</i>: Transient
</li><li> 0<i>x</i>60000000 - 0<i>xFFFFFFFF</i>: Reserved
</li></ul>
<p>
We will simply choose a <em>user deifnined value</em> for our program number. The version and procedure
numbers are set according to standard practice.
</p><p>
We still have the <tt>DIR_SIZE</tt> definition required from the local version as the size of the
directory buffer is rewquired by bith client and server programs.
</p><p>
Our new <tt>rls.h</tt> file contains:
</p><p></p><pre>#define DIR_SIZE 8192
#define DIRPROG ((u_long) 0x20000001)   /* server program (suite) number */
#define DIRVERS ((u_long) 1)    /* program version number */
#define READDIR ((u_long) 1)    /* procedure number for look-up */
</pre>
<p></p><h2><a name="SECTION003364000000000000000">
Sharing the data</a>
</h2>
<p>
We have mentioned previously that we can pass the data a simple strings. We need to define an XDR
filter routine <tt>xdr_dir()</tt> that shares the data. Recall that only one encoding and decoding
argument can be handled. This is easy and defined via the standard <tt>xdr_string()</tt> routine.
</p><p>
The XDR file, <tt>rls_xrd.c</tt>, is as follows:
</p><p></p><pre>#include &lt;rpc/rpc.h&gt;

#include "rls.h"

bool_t xdr_dir(XDR *xdrs, char *objp)

{ return ( xdr_string(xdrs, &amp;objp, DIR_SIZE) ); }
</pre>
<p></p><h3><a name="SECTION003364100000000000000">
The Server Side</a>
</h3>
<p>
We can use the original <tt>read_dir.c</tt> file. All we need to do is register the procedure and start
the server.
</p><p>
The procedure is registered with <tt>registerrpc()</tt> function. This is prototypes by:
</p><p></p><pre>registerrpc(u_long prognum /* Server program number */,
        u_long versnum /* Server version number */,
        u_long procnum /* server procedure number */,
        char *procname /* Name of remote function */,
        xdrproc_t inproc /* Filter to encode arg */,
        xdrproc_t outproc /* Filter to decode result */);
</pre>
<p>
The parameters a similarly defined as in the <tt>rpc_reg</tt> simplified interface function. We have
already discussed the setting of the parametere with the protocol <tt>rls.h</tt> header files and the
<tt>rls_xrd.c</tt> XDR filter file.
</p><p>
The <tt>svc_run()</tt> routine has also been discussed previously.
</p><p>
The full <tt>rls_svc.c</tt> code is as follows:
</p><p></p><pre>#include &lt;rpc/rpc.h&gt;
#include "rls.h"

main()
{
        extern bool_t xdr_dir();
        extern char * read_dir();

        registerrpc(DIRPROG, DIRVERS, READDIR,
                        read_dir, xdr_dir, xdr_dir);

        svc_run();
}
</pre>
<p></p><h3><a name="SECTION003364200000000000000">
The Client Side</a>
</h3>
<p>
At the client side we simply need to call the remote procedure.
 The function <tt>callrpc()</tt> does this. It is prototyped as follows:
</p><p></p><pre>callrpc(char *host /* Name of server host */,
    u_long prognum /* Server program number */,
    u_long versnum /* Server version number */,
    char *in /* Pointer to argument */,
    xdrproc_t inproc /* XDR filter to encode arg */,
    char *out /* Address to store result */
    xdr_proc_t outproc /* Filter to decode result */);
</pre>
<p>
We call a local function <tt>read_dir()</tt> which uses <tt>callrpc()</tt> to call the remote procedure that
has been registered <tt>READDIR</tt> at the server.
</p><p>
The full <tt>rls.c</tt> program is as follows:
</p><p></p><pre>/*
 * rls.c: remote directory listing client
 */
#include &lt;stdio.h&gt;
#include &lt;strings.h&gt;
#include &lt;rpc/rpc.h&gt;
#include "rls.h"

main (argc, argv)
int argc; char *argv[];
{
char    dir[DIR_SIZE];

        /* call the remote procedure if registered */
        strcpy(dir, argv[2]);
        read_dir(argv[1], dir); /* read_dir(host, directory) */

        /* spew-out the results and bail out of here! */
        printf("%s\n", dir);

        exit(0);
}

read_dir(host, dir)
char   *dir, *host;
{
        extern bool_t xdr_dir();
        enum clnt_stat clnt_stat;

        clnt_stat = callrpc ( host, DIRPROG, DIRVERS, READDIR,
                        xdr_dir, dir, xdr_dir, dir);
        if (clnt_stat != 0) clnt_perrno (clnt_stat);
}
</pre>
<p></p><h1><a name="SECTION003370000000000000000">
Exercise</a>
</h1>
<p>
<b>Exercise 12833</b></p>
<p>
Compile and run the remote directory example <tt>rls.c</tt> <em>etc</em>. Run both the client ande srever
locally and if possible over a network.</p>
<p>
<br></p><hr>
<address>
<i>Dave Marshall</i>
<br><i>1/5/1999</i>
</address>


</body></html>