Server Side of the Simplified Interface
The server program using the simplified interface is straightforward. The server calls rpc_reg() to register the procedure to be called. It then calls svc_run(), the RPC library's remote procedure dispatcher, to wait for requests to arrive.
rpc_reg() has the following arguments:
rpc_reg ( rpcprog_t prognum /* Server program number */ rpcvers_t versnum /* Server version number */ rpcproc_t 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 */ ); |
svc_run() invokes service procedures in response to RPC call messages. The dispatcher in rpc_reg() decodes remote procedure arguments and encodes results, using the XDR filters specified when the remote procedure was registered. Some notes about the server program include:
Most RPC applications follow the naming convention of appending a _1 to the function name. The sequence _n is added to the procedure names to indicate the version number n of the service.
The argument and result are passed as addresses. This is true for all functions that are called remotely. Passing NULL as a result of a function means that no reply is sent to the client, because NULL indicates that there is no reply to send.
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.
Only a single argument is allowed. If there are multiple elements of data, they should be wrapped inside a structure that can then be passed as a single entity.
The procedure is registered for each transport of the specified type. If the type parameter is (char *)NULL, the procedure is registered for all transports specified in NETPATH.
Hand-Coded Registration Routine
You can sometimes implement faster or more compact code than can rpcgen. rpcgen 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 svc_run() to service requests.
#include <stdio.h> #include <rpc/rpc.h> #include <rpcsvc/rusers.h> void *rusers(); main() { if(rpc_reg(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, rusers, xdr_void, xdr_u_int, "visible") == -1) { fprintf(stderr, "Couldn't Register\n"); exit(1); } svc_run(); /* Never returns */ fprintf(stderr, "Error: svc_run returned!\n"); exit(1); } |
rpc_reg() can be called as many times as is needed to register different programs, versions, and procedures.
Passing Arbitrary Data Types
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 the byte orders or structure layout conventions of different machines. RPC always converts these structures 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 rpc_call() and rpc_reg() can specify an XDR primitive procedure, like xdr_u_int(), 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.
The XDR Primitive Type Routines are:
- 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()
- xdr_hyper()
- xdr_u_hyper()
The fixed-width integer types found in int_types.h, the routines xdr_char(), xdr_short(), xdr_int(), and xdr_hyper() (and the unsigned versions of each) have equivalent functions with names familiar to ANSI C, as indicated in the following table:
Table 4-1 Primitive Type Equivalences
Function | Equivalent |
---|---|
xdr_char() | xdr_int8_t() |
xdr_u_char() | xdr_u_int8_t() |
xdr_short() | xdr_int16_t() |
xdr_u_short() | xdr_u_int16_t() |
xdr_int() | xdr_int32_t() |
xdr_u_int() | xdr_u_int32_t() |
xdr_hyper() | xdr_int64_t() |
xdr_u_hyper() | xdr_u_int64_t() |
The nonprimitive xdr_string(), which takes more than two parameters, is called from xdr_wrapstring().
The following example of a programmer-supplied routine contains the calling arguments of a procedure.
struct simple { int a; short b; } simple; |
The XDR routine xdr_simple() translates the argument structure as shown in the following code example.
Example 4-3 xdr_simple Routine
rpcgen can automatically generate an equivalent routine.
An XDR routine returns nonzero (a C TRUE) if it completes successfully, and zero otherwise. A complete description of XDR is provided in Appendix C, XDR Protocol Specification.