リモートプロシージャへ渡すデータ型とリモートプロシージャから受け取るデータ型は、事前に定義した型あるいはプログラマが定義する型の任意のものが可能です。RPC では、個々のマシンに固有のバイト順序や構造体のデータレイアウトに関係なく、任意のデータ構造を扱うことができます。データは、XDR (external data representation: 外部データ表現) 形式という標準データ形式に変換してからトランスポートに送信されます。マシン固有のデータ形式から XDR 形式に変換することをシリアライズといい、反対に XDR 形式からマシン固有のデータ形式に変換することをデシリアライズといいます。
rpc_call() と rpc_reg() の引数で変換ルーチンを指定するときは、 xdr_u_int() のような XDR プリミティブを指定することも、引数として渡された構造体全体を処理するようなユーザーが作成した変換ルーチンを指定することもできます。引数の変換ルーチンは 2 つの引数を取ります。1 つは変換結果へのポインタで、もう 1 つは XDR ハンドルへのポインタです。
int_types.h 内にある固定幅の整数タイプに慣れている ANSI C プログラマにとって都合がよいように、ルーチン xdr_char()、 xdr_short()、 xdr_int()、およびxdr_hyper() (および、それぞれの符号なしバージョン) には、次の表で示すように、ANSI C を連想させる名前の付いた同等の関数があります。
表 4–1 プリミティブタイプの等価関数
関数名 |
等価関数名 |
---|---|
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() |
xdr_wrapstring() から呼び出す xdr_string() はプリミティブではなく、3 つ以上の引数を取ります。
ユーザーが作成する変換ルーチンの例を次に示します。
struct simple { int a; short b; } simple;
この構造体で渡された引数を変換する XDR ルーチン xdr_simple() は、次に示すようになります。
#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); }
rpcgen でも、同じ機能を持つ変換ルーチンを自動生成できます。
XDR ルーチンは、データ変換に成功した場合はゼロ以外の値 (C では TRUE) を返し、失敗した場合はゼロを返します。XDR についての詳細は、付録 C 「XDR プロトコル仕様」を参照してください。
たとえば、可変長の整数配列を送るときは、配列へのポインタと配列サイズを次のような構造体にパックします。
struct varintarr { int *data; int arrlnth; } arr; |
この配列を変換するルーチン xdr_varintarr() を次に示します。
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)); } |
xdr_array() に渡す引数は、XDR ハンドル、配列へのポインタ、配列サイズへのポインタ、配列サイズの最大値、配列要素のサイズ、配列要素を変換する XDR ルーチンへのポインタです。配列サイズが前もってわかっている場合は、次のように xdr_vector() を使用します。
int intarr[SIZE]; bool_t xdr_intarr(xdrsp, intarr) XDR *xdrsp; int intarr[]; { return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int), xdr_int)); } |
XDR ルーチンでシリアライズすると、データが 4 バイトの倍数になるように変換されます。たとえば、文字配列を変換すると、各文字が 32 ビットを占有するようになります。xdr_bytes() は、文字をパックするルーチンです。これは、xdr_array() の最初の 4 つの引数と同様の引数を取ります。
NULL で終わる文字列は xdr_string() で変換します。このルーチンは、長さの引数がない xdr_bytes() ルーチンのようなものです。xdr_string() は、文字列をシリアライズするときは strlen() で長さを取り出し、デシリアライズするときは NULL で終わる文字列を生成します。
次の例では、組み込み関数 xdr_string () とxdr_reference() を呼び出して、文字列へのポインタと、前の例で示した struct simple へのポインタを変換しています。
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); } |
ここで、xdr_reference() の代わりに xdr_simple() を呼び出してもよいことに注意してください。