この節では、XDR プリミティブの概要を述べます。始めにメモリー割り当てと基本データ型を説明し、次に合成データ型を説明します。最後に、XDR ユーティリティについて説明します。XDR のプリミティブとユーティリティのインタフェースはインクルードファイルで定義されています。<rpc/xdr.h> は <rpc/rpc.h> から自動的にインクルードされます。
XDR ルーチンを使用するときは、前もってメモリーを割り当てておかなければならない (または、必要なメモリーサイズを決定しておかなければならない) 場合があります。XDR 変換ルーチンで使用するメモリーの割り当てや割り当て解除が必要なときのために、xdr_sizeof() というルーチンが提供されています。xdr_sizeof() は、XDR フィルタルーチン (func()) がデータ (data()) の符号化や復号化で使用するバイト数を返します。xdr_sizeof() が返す値には、RPC ヘッダーやレコードマークは含まれていませんので、必要なメモリーサイズを正確に求めるには、それらのバイト数も加えなければなりません。エラーが起こった場合、xdr_sizeof() はゼロを返します。
xdr_sizeof(xdrproc_t func, void *data) |
xdr_sizeof() は、RPC 環境以外で XDR を使用するアプリケーションでメモリーを割り当てるとき、トランスポートプロトコルを選択するとき、下位レベルの RPC を使用してクライアント作成関数やサーバー作成関数を実行するときに特に便利です。
例 A-5 と 例 A-6で、xdr_sizeof()の 2 通りの使用方法を説明します。
#include <rpc/rpc.h> /* * この関数への入力引数は、CLIENT ハンドル、XDR 関数、XDR 変換を行う * データへのポインタです。XDR 変換を行うデータが、CLIENT ハンドルに * 結合しているトランスポートで送信可能な大きさの場合は TRUE、 * 大き過ぎて送信不可能な場合は FALSE を返します。 */ bool_t cansend(cl, xdrfunc, xdrdata) CLIENT *cl; xdrproc_t xdrfunc; void *xdrdata; { int fd; struct t_info tinfo; if (clnt_control(cl, CLGET_FD, &fd) == -1) { /* ハンドルの clnt_control() エラー */ return (FALSE); } if (t_getinfo(fd, &tinfo) == -1) { /* ハンドルの t_getinfo() エラー */ return (FALSE); } else { if (tinfo.servtype == T_CLTS) { /* * 現在使用しているのは非接続型トランスポートです。 * xdr_sizeof() を使用して、メモリー要求がこのトランスポートでは * 大き過ぎないか調べます。 */ switch(tinfo.tsdu) { case 0: /* TSDU の概念なし */ case -2: /* 通常データの送信不可能 */ return (FALSE); break; case -1: /* TSDU サイズの制限なし */ return (TRUE); break; default: if (tinfo.tsdu < xdr_sizeof(xdrfunc, xdrdata)) return (FALSE); else return (TRUE); } } else return (TRUE); } } |
例 A-6 は、xdr_sizeof() の使用例 2 になります。
#include <sys/statvfs.h> #include <sys/sysmacros.h> /* * この関数への入力引数は、ファイル名、XDR 関数、XDR 変換を行うデータへの * ポインタです。この関数は、ファイルが置かれているファイルシステムに、 * データを XDR 変換するのに必要な空間が残っていれば TRUE を返します。 * ファイルシステムに関して statvfs(2) で得られる情報はブロック数単位 * なので、xdr_sizeof() の戻り値もバイト数からディスクブロック数に * 変換しなければならないことに注意してください。 */ bool_t canwrite(file, xdrfunc, xdrdata) char *file; xdrproc_t xdrfunc; void *xdrdata; { struct statvfs s; if (statvfs(file, &s) == -1) { /* ハンドルの statvfs() エラー */ return (FALSE); } if (s.f_bavail >= btod(xdr_sizeof(xdrfunc, xdrdata))) return (TRUE); else return (FALSE); } |
XDR ライブラリでは、整数を対応する外部表現に変換するプリミティブが提供されます。プリミティブが変換の対象とする整数は、次の組み合わせで表されます。
[signed, unsigned] * [short, int, long] |
具体的には次の 8 つのプリミティブが提供されています。
bool_t xdr_char(xdrs, op) XDR *xdrs; char *cp; bool_t xdr_u_char(xdrs, ucp) XDR *xdrs; unsigned char *ucp; bool_t xdr_int(xdrs, ip) XDR *xdrs; int *ip; bool_t xdr_u_int(xdrs, up) XDR *xdrs; unsigned *up; bool_t xdr_long(xdrs, lip) XDR *xdrs; long *lip; bool_t xdr_u_long(xdrs, lup) XDR *xdrs; u_long *lup; bool_t xdr_short(xdrs, sip) XDR *xdrs; short *sip; bool_t xdr_u_short(xdrs, sup) XDR *xdrs; u_short *sup;
最初の引数 xdrs は、XDR のストリームハンドルです。第 2 引数は、ストリームへ渡すデータのアドレス、または、ストリームからデータを受け取るアドレスです。どのルーチンも、変換に成功すれば TRUE、失敗すれば FALSE を返します。
XDR ライブラリでは、C の浮動小数点型データのプリミティブも提供されます。
bool_t xdr_float(xdrs, fp) XDR *xdrs; float *fp; bool_t xdr_double(xdrs, dp) XDR *xdrs; double *dp; |
最初の引数 xdrs は、XDR のストリームハンドルです。第 2 引数は、ストリームへ渡す浮動小数点データのアドレスまたはストリームから浮動小数点データを受け取るアドレスです。どちらのルーチンも、変換に成功すれば TRUE、失敗すれば FALSE を返します。
数値の表現形式は、浮動小数点に関する IEEE 標準規約に従っているため、IEEE 準拠の表現形式からマシン固有の表現形式に復号化したり、暗号化したりすると、エラーが起こる場合があります。
XDR ライブラリでは、一般の列挙型に対するプリミティブを提供しています。このプリミティブでは、C の enum
型のマシン内部表現が C の整数と同じであるとみなしています。ブール型は enum
型の重要な一例です。ブール値の外部表現は常に TRUE (1) と FALSE (0) です。
#define bool_t int #define FALSE 0 #define TRUE 1 #define enum_t int bool_t xdr_enum(xdrs, ep) XDR *xdrs; enum_t *ep; bool_t xdr_bool(xdrs, bp) XDR *xdrs; bool_t *bp; |
第 2 引数 ep と bp は、ストリーム xdrs へ渡すデータのアドレス、または、ストリーム xdrs からデータを受け取るアドレスです。
ときには、データが一切渡されず要求されていなくても、XDR ルーチンを RPC システムに提供しなければならない場合があります。ライブラリでは、そのためのルーチンを提供しています。
bool_t xdr_void(); /* 常に TRUE を返す */ |
合成データ型、または、複合データ型を変換するプリミティブは、これまでに説明したプリミティブより多くの引数を必要とし、より複雑な機能を実行します。この節では、文字列、配列、共用体、構造体へのポインタに対するプリミティブを説明します。
合成データ型のプリミティブでは、メモリー管理を使用する場合があります。多くの場合、XDR_DECODEを指定してデータをデシリアライズすると、メモリーが割り当てられます。そのため、XDR パッケージではメモリー割り当てを解除する方法を提供しなければなりません。メモリー割り当ての解除は、XDR_FREE という XDR 処理で行います。XDRの 処理内容には、XDR_ENCODE、XDR_DECODE、XDR_FREE の 3 つがあります。
C 言語では、文字列が、NULL コードで終わるバイトシーケンスと定義されています。NULL コードは、文字列の長さを求めるときは計算に入れません。ところが、文字列を引き渡したり操作したりするときは、文字へのポインタが使用されます。そのため、XDRライブラリでは文字列を文字シーケンスではなく、char *
と定義しています。文字列の外部表現は、内部表現とは大きく異なります。
文字列は内部では文字へのポインタで表現されますが、外部では ASCII 文字シーケンスで表現されます。この 2 つの表現形式の間の変換は、ルーチン xdr_string() で実行します。
bool_t xdr_string(xdrs, sp, maxlength) XDR *xdrs; char **sp; u_int maxlength; |
最初の引数 xdrs は、XDR のストリームハンドルです。第 2 引数 sp は文字列へのポインタ (データ型は char **
) です。第 3 引数 maxlength は、符号化または復号化の対象とする最大バイト数です。通常、この値はプロトコルで決まります。たとえば、あるプロトコル仕様では、ファイル名は最大 255 文字までとされています。文字数が maxlength の値を超えていれば FALSE、超えていなければ TRUE が返されます。
xdr_string() の機能は、この節でこれまでに説明した他の変換ルーチンと同様です。処理内容が XDR_ENCODE の場合は最も簡単です。引数 sp はある長さの文字列を指しています。この文字列の長さが maxlength を超えていなければ、この文字列がシリアライズされます。
デシリアライズの場合はもう少し複雑です。最初に、ストリームから取り込む文字列の長さを決定します。文字列の長さは maxlength を超えることはできません。次に sp をデレファレンスします。その値が NULL の場合は、適切なサイズの文字列を割り当てて、*sp がその文字列を指すように設定します。*sp の元々の値が NULL でない場合は、デシリアライズしたデータを入れるターゲットエリアが既に割り当てられており、maxlength 以下の長さの文字列をそこに格納できるものとみなします。どちらの場合も、文字列が復号化されてターゲットエリアに保存されます。次に、文字列の最後に NULL コードが付加されます。
XDR_FREE 処理の場合は、sp をデレファレンスして文字列を取り出します。その文字列が NULL 文字列でなければ、領域を解放して *sp を NULL に設定します。この処理を実行するときは、xdr_string() は引数 maxlength を無視します。
空の文字列 ("") を XDR 変換することはできますが、NULL 文字列を XDR 変換はできません。
文字列よりも可変長バイト配列を使用する方が便利な場合があります。バイト配列は、次の 3 つの点で文字列と異なっています。(1) 配列の長さ(バイトカウント) を符号なし整数として明示的に保持している。(2) バイトシーケンスが NULL コードで終了しない。(3) データの外部表現と内部表現が一致する。バイト配列の内部表現と外部表現との変換には、プリミティブ xdr_bytes() を使用します。
bool_t xdr_bytes(xdrs, bpp, lp, maxlength) XDR *xdrs; char **bpp; u_int *lp; u_int maxlength; |
このルーチンの第 1、第 2、第 4 引数はそれぞれ、xdr_string() の第 1、第 2、第 3 引数と同じです。シリアライズの場合は、lp をデレファレンスしてバイトシーケンスの長さを得ます。デシリアライズの場合は、*lp にバイトシーケンスの長さが設定されます。
XDR ライブラリパッケージでは、任意の要素で構成される配列を処理するプリミティブが提供されています。xdr_bytes() ルーチンは一般の配列のサブセットを処理します。すなわち、xdr_bytes() ルーチンでは、配列要素のサイズは 1 に決まっており、各要素の外部記述も組み込まれています。一般の配列に対するプリミティブ xdr_array() の引数は、xdr_bytes() の引数より 2 つ多く、配列要素のサイズと、各要素を変換する XDR ルーチンとが渡されます。渡されたXDR ルーチンは、配列の各要素の符号化または復号化のときに呼び出されます。
bool_t xdr_array(xdrs, ap, lp, maxlength, elementsize, xdr_element) XDR *xdrs; char **ap; u_int *lp; u_int maxlength; u_int elementsize; bool_t (*xdr_element)(); |
引数 ap は、配列へのポインタのアドレスです。配列をデシリアライズするときに *ap が NULL の場合は、適切なサイズの配列が割り当てられ、*ap はその配列を指すように設定されます。配列をシリアライズするときは、配列の要素数を *lp から取り出します。配列をデシリアライズするときは、*lp には配列の長さが設定されます。引数 maxlength は、配列に入れることができる最大要素数です。elementsiz は、配列の各要素のサイズ (バイト数) です。C の sizeof() 関数を使用してこの値を調べることができます。xdr_element() ルーチンは、配列の各要素のシリアライズ、デシリアライズ、解放を行うときに呼び出されます。
このほかの合成データ型の説明の前に、3 つのサンプルプログラムを説明します。
ネットワークに接続したマシンのユーザーは、次の 3 つの項目によって識別できます。(a) マシン名。たとえば、krypton。(b) ユーザーの UID。これについては、geteuid(2) のマニュアルページを参照してください。(c) ユーザーが所属するグループ番号。これについては、getgroups(2) のマニュアルページを参照してください。これらの識別情報を持つ構造体と、それに対する XDR ルーチンは例 A-7 のようにコーディングできます。
struct netuser { char *nu_machinename; int nu_uid; u_int nu_glen; int *nu_gids; }; #define NLEN 255 /* マシン名は 255 文字以下 */ #define NGRPS 20 /* ユーザーが所属するグループ数は 20 以下 */ bool_t xdr_netuser(xdrs, nup) XDR *xdrs; struct netuser *nup; { return(xdr_string(xdrs, &nup->nu_machinename, NLEN) && xdr_int(xdrs, &nup->nu_uid) && xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen, NGRPS, sizeof (int), xdr_int)); } |
ネットワークユーザーのグループは、netuser 構造体の配列で表すことができます。構造体の宣言と、それに対する XDR ルーチンは 例 A-8 のようになります。
struct party { u_int p_len; struct netuser *p_nusers; }; #define PLEN 500 /* グループに所属するユーザー数の上限 */ bool_t xdr_party(xdrs, pp) XDR *xdrs; struct party *pp; { return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN, sizeof (struct netuser), xdr_netuser)); } |
main に対するよく知られた引数 argc と argv を持つ構造体を作成し、その構造体の配列にコマンドヒストリを保存することができます。構造体の宣言と、その XDR ルーチンは 例 A-9 のようになります。
struct cmd { u_int c_argc; char **c_argv; }; #define ALEN 1000 /* argc は 1000 以下 */ #define NARGC 100 /* 各コマンドの args は 100 以下 */ struct history { u_int h_len; struct cmd *h_cmds; }; #define NCMDS 75 /* ヒストリは 75 コマンドまで */ bool_t xdr_wrapstring(xdrs, sp) XDR *xdrs; char **sp; { return(xdr_string(xdrs, sp, ALEN)); } bool_t xdr_cmd(xdrs, cp) XDR *xdrs; struct cmd *cp; { return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC, sizeof (char *), xdr_wrapstring)); } bool_t xdr_history(xdrs, hp) XDR *xdrs; struct history *hp; { return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS, sizeof (struct cmd), xdr_cmd)); } |
このプログラムで最もむずかしいのは、xdr_string() を呼び出すためのルーチン xdr_wrapstring() が必要な点です。xdr_array() が配列要素記述ルーチンを呼び出すときは引数が 2 つしか渡されないため、xdr_string() の第 3 引数を提供するルーチン xdr_wrapstring() が必要になります。
これまでの説明で XDR ライブラリの再帰的性質が明らかになりました。その他の合成データ型の変換も見てみましょう。
プロトコルによっては、サーバーからクライアントにハンドルが渡され、クライアントは後からハンドルをサーバーに送り返します。クライアントではハンドルの内容を調べることはなく、受け取ったものをそのまま送り返します。すなわち、ハンドルは隠されたデータ (内容が隠されたデータ) です。固定長の隠されたデータを記述するには、xdr_opaque() プリミティブを使用します。
bool_t xdr_opaque(xdrs, p, len) XDR *xdrs; char *p; u_int len; |
引数 p はデータのアドレスです。len は隠れたオブジェクトに入っているバイト数です。隠されたデータの定義からすると、実際に隠れたオブジェクトに入っているデータはマシン間で移植不可能です。
SunOS/SVR4 には、隠されたデータの操作用にもう 1 つのルーチンが提供されています。そのルーチン xdr_netobj() は xdr_opaque() と同様にカウント付きの隠されたデータを送信します。 例 A-10 に、xdr_netobj() の構文を示します。
struct netobj { u_int n_len; char *n_bytes; }; typedef struct netobj netobj; bool_t xdr_netobj(xdrs, np) XDR *xdrs; struct netobj *np; |
xdr_netobj() はフィルタプリミティブで、可変長の隠されたデータとその外部表現との変換を行います。引数 np は netobj() 構造体のアドレスです。netobj() 構造体には、隠されたデータの長さと隠されたデータへのポインタが入っています。長さは、MAX_NETOBJ_SZ バイトを超えることはできません。変換に成功すれば TRUE、失敗すれば FALSE が返されます。
xdr_vector() ライブラリでは、例 A-11サンプルプログラムで示すように、固定長配列に対するプリミティブ xdr_vector()が提供されています。
#define NLEN 255 /* マシン名は 255 文字以下 */ #define NGRPS 20 /* ユーザーは正確に 20 のグループに所属 */ struct netuser { char *nu_machinename; int nu_uid; int nu_gids[NGRPS]; }; bool_t xdr_netuser(xdrs, nup) XDR *xdrs; struct netuser *nup; { int i; if (!xdr_string(xdrs, &nup->nu_machinename, NLEN)) return(FALSE); if (!xdr_int(xdrs, &nup->nu_uid)) return(FALSE); if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int), xdr_int)) return(FALSE); return(TRUE); } |
XDR ライブラリは識別型の共用体もサポートしています。識別型の共用体は、C の共用体に、共用体の「アーム」を選択する enum_t
型の値が付加されたものです。
struct xdr_discrim { enum_t value; bool_t (*proc)(); }; bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm) XDR *xdrs; enum_t *dscmp; char *unp; struct xdr_discrim *arms; bool_t (*defaultarm)(); /* may equal NULL */ |
このルーチンでは、最初に *dscmp にある要素識別子を変換します。要素識別子は常に enum_t
型です。次に、*unp にある共用体が変換されます。引数 arms は xdr_discrim 構造体配列へのポインタです。各構造体には、[value,proc] のペアが入っています。共用体の要素識別子が対応する value と一致すれば、それに対する proc() が呼び出されて共用体が変換されます。xdr_discrim 構造体配列の終わりは、ルーチンの値が NULL(0) なので判別できます。arms 配列の中に要素識別子に一致するものがない場合は、defaultarm() 手続きが NULL でなければそれが呼び出されます。NULL の場合は FALSE を返します。
共用体のデータ型は、整数、文字へのポインタ (文字列)、gnumbers 構造体のどれかで、共用体と現在の型は構造体で宣言されています。宣言は次のようになります。
enum utype {INTEGER=1, STRING=2, GNUMBERS=3}; struct u_tag { enum utype utype; /* 共用体の要素識別子 */ union { int ival; char *pval; struct gnumbers gn; } uval; }; |
例 A-12 では、合成データと XDR 手続きで、識別型共用体のシリアライズ (またはデシリアライズ) を行います。
struct xdr_discrim u_tag_arms[4] = { {INTEGER, xdr_int}, {GNUMBERS, xdr_gnumbers} {STRING, xdr_wrapstring}, {__dontcare__, NULL} /* アームの最後は常に NULL の xdr_proc */ } bool_t xdr_u_tag(xdrs, utp) XDR *xdrs; struct u_tag *utp; { return(xdr_union(xdrs, &utp->utype, &utp->uval, u_tag_arms, NULL)); } |
xdr_gnumbers() については「XDR ライブラリ」の節で説明し、xdr_wrapstring() はサンプルプログラム C に示しました。この例では、xdr_union()のデフォルトの arm 引数 (最後の引数) には NULL を渡しています。したがって、共用体の要素識別子の値は、u_tag_arms 配列に表示された値のどれかになります。また、この例から、arm 配列の要素はソートされていなくてもよいことがわかります。
要素識別子は、この例では連続した値を取っていますが、連続していなくてもかまいません。要素識別子の型の各要素に、明示的に整数値を割り当てておくと、要素識別子の外部表現として明記でき、異なる C コンパイラでも要素識別子が同じ値で出力されることが保証されます。
この節の他のプリミティブを使用して xdr_union() を作成してください。
C では、構造体の中に、別の構造体へのポインタを入れることがよくあります。プリミティブ xdr_reference() を使用すると、そのように参照される構造体を簡単にシリアライズ (またはデシリアライズ) できます。
bool_t xdr_reference(xdrs, pp, size, proc) XDR *xdrs; char **pp; u_int ssize; bool_t (*proc)(); |
引数 pp は構造体へのポインタのアドレスです。引数 ssize は構造体のサイズ (バイト数) です。C の sizeof() 関数を使用してこの値を調べることができます。引数 proc() は構造体を記述する XDR ルーチンです。データを復号化するとき、*pp が NULL の場合は記憶領域が割り当てられます。
プリミティブ xdr_struct() では、構造体内の構造体を記述する必要はありません。ポインタだけで十分です。
xdr_array() を使用して xdr_reference() を作成してください。
xdr_reference() と xdr_array() のデータ外部表現は互換ではありません。
人の名前、および、その人の総資産と負債の入った gnumbers 構造体へのポインタとで構成される次のような構造体があるとします。
struct pgn { char *name; struct gnumbers *gnp; }; |
これに対する XDR ルーチンは次のようになります。
bool_t xdr_pgn(xdrs, pp) XDR *xdrs; struct pgn *pp; { return(xdr_string(xdrs, &pp->name, NLEN) && xdr_reference(xdrs, &pp->gnp, sizeof(struct gnumbers), xdr_gnumbers)); } |
C のプログラマは多くのアプリケーションで、ポインタの値に 2 つの意味を持たせています。一番多い例としては、ポインタ値が NULL (ゼロ) の場合はデータが不要なことを意味する方法ですが、アプリケーションごとにさまざまな解釈ができます。実際に C プログラマは、ポインタ値の解釈をオーバロードすることにより、効率よく識別型共用体を実現しています。たとえば、サンプルプログラム E では、gnp のポインタ値を NULL にすれば、その人の総資産と負債が未知であると解釈できます。すなわち、ポインタ値には 2 つの意味があります。データがあるかどうかを示し、もしあるとしたらメモリーのどこにあるかを示します。リンクリストは、アプリケーション固有のポインタ解釈の極端な例です。
プリミティブ xdr_reference() はシリアライズの際に、値が NULL のポインタに特別な意味を持たせることができません。そのため、データをシリアライズするときに、xdr_reference() に値が NULL のポインタのアドレスを渡すと、多くの場合メモリーフォルトを引き起こし、UNIX システムではコアダンプが起こります。
xdr_pointer() では NULL ポインタも正しく処理できます。
XDR ストリームの操作は、次に示すプリミティブで行います。
u_int xdr_getpos(xdrs) XDR *xdrs; bool_t xdr_setpos(xdrs, pos) XDR *xdrs; u_int pos; xdr_destroy(xdrs) XDR *xdrs; |
ルーチン xdr_getpos()はデータストリーム内の現在位置を符号なし整数で返します。
警告 : XDR ストリームの中には、xdr_getpos()の返す値が意味を持たないものがあります。その場合は、-1 が返されます (ただし、-1 も正当な値です)。
ルーチンxdr_setpos() はストリーム位置を posに設定します。
警告 : XDR ストリームの中には、xdr_setpos() で位置設定ができないものもあります。その場合は、FALSE が返されます。指定した位置が適用範囲外の場合もエラーとなります。設定位置の適用範囲はストリームごとに異なります。
XDR ストリームを廃棄するには、プリミティブ xdr_destroy() を使用します。このルーチンを呼び出した後で、ストリームを使用した場合の動作は未定です。
処理内容 (XDR_ENCODE、XDR_DECODE、XDR_FREE のどれか) を利用して XDR ルーチンを最適化したい場合があります。XDR の処理内容は、常に xdrs->x_op に入っています。「リンクリスト」の節には、xdrs->x_op フィールドを活用するサンプルプログラムが示されています。
XDR ストリームは、適切な作成ルーチンを呼び出すことで得られます。作成ルーチンへの引数でストリームのさまざまな特性が決まります。現在、データのシリアライズとデシリアライズに使用できるストリームには、標準入出力 FILE ストリーム、レコードストリーム、UNIX ファイル、メモリーがあります。
XDR ストリームは、xdrstdio_create() を使用して標準入出力とのインタフェースをとることができます。
#include <stdio.h> #include <rpc/rpc.h> /* XDR は RPC のサブセット */ void xdrstdio_create(xdrs, fp, xdr_op) XDR *xdrs; FILE *fp; enum xdr_op x_op; |
xdrstdio_create() は、xdrs が指す XDR ストリームを初期化します。XDR ストリームは、標準入出力ライブラリとのインタフェースが可能です。引数 fp はオープンしているファイル、x_op は XDR の処理内容です。
メモリーストリームを作成すると、メモリーの特定領域とのデータストリームが使用できます。
#include <rpc/rpc.h> void xdrmem_create(xdrs, addr, len, x_op) XDR *xdrs; char *addr; u_int len; enum xdr_op x_op; |
xdrmem_create() ルーチンは、ローカルメモリー内の XDR ストリームを初期化します。引数 addr は使用するメモリーを指します。引数 len はメモリーの大きさ (バイト数) です。引数 xdrs と x_op は、xdrstdio_create() の同名の引数と同じです。現在、RPC ではデータグラムの実現に xdrmem_create() を使用しています。TLI ルーチン t_sndndata() を呼び出す前に、完全な呼び出しメッセージ (または応答メッセージ) がメモリーに構築されます。
レコードストリームは、レコードマーク標準の上に構築される XDR ストリームです。レコードマーク標準は、UNIX ファイル、または、4.2 BSD 接続インタフェースの上に構築されます。
#include <rpc/rpc.h> /* XDR は RPC のサブセット */ xdrrec_create(xdrs, sendsize, recvsize, iohandle, readproc, writeproc) XDR *xdrs; u_int sendsize, recvsize; char *iohandle; int (*readproc)(), (*writeproc)(); |
xdrrec_create() は、任意の長さの双方向レコードシーケンスが可能な XDR ストリームインタフェースを提供します。レコードの内容は、XDR 形式のデータと考えられます。レコードストリームは、主に RPC と TCP 接続とのインタフェースとして使用されますが、通常の UNIX ファイルとのデータ入出力にも使用できます。
引数 xdrs は、これまでに説明した同名の引数と同じです。レコードストリームでは、標準入出力ストリームと同様のデータバッファリングを行います。引数 sendsize と recvsize には、それぞれ送信バッファと受信バッファのサイズ (バイト数) を指定します。この値がゼロ (0) の場合は、あらかじめ指定されたデフォルトのバッファサイズが使用されます。バッファにデータを読み込んだり、データをフラッシュしたりするときは、ルーチン readproc()または writeproc() を呼び出します。この 2 つのルーチンの使用方法と機能は、UNIXのシステムコール read() と write() に似ていますが、第 1 引数には隠されたデータ iohandle を渡します。その後の 2 つの引数 (および nbytes) と、戻り値 (バイトカウント) はシステムルーチンと同じです。次の xxx()を readproc() または writeproc() とすると、その形式は次のようになります。
/* 実際に伝送したバイト数を返す。エラーのときの戻り値は -1 */ int xxx(iohandle, buf, len) char *iohandle; char *buf; int nbytes; |
XDR ストリームには、バイトストリームのレコードを区切る方法が提供されています。XDR ストリームを作成するのに必要な抽象データ型については、「XDR ストリームの作成」を参照してください。XDRストリームレコードを区切るのに使用する RPC プロトコルについては、「レコードマーク標準」を参照してください。
レコードストリームに特有なプリミティブは以下のとおりです。
bool_t xdrrec_endofrecord(xdrs, flushnow) XDR *xdrs; bool_t flushnow; bool_t xdrrec_skiprecord(xdrs) XDR *xdrs; bool_t xdrrec_eof(xdrs) XDR *xdrs; |
xdrrec_endofrecord() ルーチンを呼び出すと、現在出力しているデータにレコードマークが付けられます。引数 flushnow に TRUE を指定すると、ストリームの writeproc() が呼び出されます。TRUE を指定しないと、出力バッファがいっぱいになったときに writeproc() が呼び出されます。
xdrrec_skiprecord() ルーチンを呼び出すと、入力ストリーム内の現在位置が、現在レコードの境界まで移動し、ストリーム内の次のレコードの始めに位置します。
ストリームの入力バッファにデータがなくなると、xdrrec_eof() ルーチンから TRUE が返されます。ストリームの元のファイル記述子にもデータが残っていないという意味ではありません。