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 rpc_call() and rpc_reg() can specify an XDR primitive procedure, like xdr_u_long(), 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.
Table 4-1 XDR Primitive Type Routines
XDR Primitive Routines |
|||
---|---|---|---|
The nonprimitive xdr_string(), which takes more than two parameters, is called from xdr_wrapstring().
For an example of a programmer-supplied routine, the structure:
struct simple { int a; short b; } simple;
contains the calling arguments of a procedure. The XDR routine xdr_simple() translates the argument structure as shown in Example 4-3.
#include <rpc/rpc.h> #include "simple.h" bool_t xdr_simple(xdrsp, simplep) XDR *xdrsp; struct simple *simplep; { if (!xdr_int(xdrsp, &simplep->a)) return (FALSE); if (!xdr_short(xdrsp, &simplep->b)) return (FALSE); return (TRUE); } |
An equivalent routine can be generated automatically by rpcgen.
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."
Table 4-2Prefabricated Routines | ||
---|---|---|
xdr_array() |
xdr_bytes() |
xdr_reference() |
xdr_vector() |
xdr_union() |
xdr_pointer() |
xdr_string() |
xdr_opaque() |
For example, to send a variable-sized array of integers, it is packaged in a structure containing the array and its length:
struct varintarr { int *data; int arrlnth; } arr;
Translate the array with xdr_varintarr(), as shown in Example 4-4.
bool_t xdr_varintarr(xdrsp, arrp) XDR *xdrsp; struct varintarr *arrp; { return(xdr_array(xdrsp, (caddr_t)&arrp->data, (u_int *)&arrp->arrlnth, MAXLEN, sizeof(int), xdr_int)); }
The arguments of xdr_array() 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 xdr_vector(), as shown in Example 4-5.
int intarr[SIZE]; bool_t xdr_intarr(xdrsp, intarr) XDR *xdrsp; int intarr[]; { return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int), xdr_int)); }
XDR converts quantities to 4-byte multiples when serializing. For arrays of characters, each character occupies 32 bits. xdr_bytes() packs characters. It has four parameters similar to the first four parameters of xdr_array().
Null-terminated strings are translated by xdr_string(). It is like xdr_bytes() with no length parameter. On serializing it gets the string length from strlen(), and on deserializing it creates a null-terminated string.
Example 4-6 calls the built-in functions xdr_string() and xdr_reference(), which translates pointers to pass a string, and struct
simple from the previous examples.
struct finalexample { char *string; struct simple *simplep; } finalexample; bool_t xdr_finalexample(xdrsp, finalp) XDR *xdrsp; struct finalexample *finalp; { if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN)) return (FALSE); if (!xdr_reference( xdrsp, &finalp->simplep, sizeof(struct simple), xdr_simple)) return (FALSE); return (TRUE); }
Note that xdr_simple() could have been called here instead of xdr_reference().