next up previous contents
Next: デーモンプロセス Up: ネットワークプログラムを書く Previous: データグラム型ソケット通信

リモートプロシジャコール

サーバ・クライアントモデルを実現するための便利な方法にリモートプロシ ジャコールrpcという方法があります。これは別の計算機にあるサブルーチン を呼び出す手順として実装されています。呼び出す側は相手の計算機の名前、 プログラム番号、バージョン番号、手続き番号を指定して関数を選びます。引 数を渡す場合、計算機の種類の違いによる数値表現の差を吸収するためにrpc はネットワーク上の表現に置き換えます。XDRと呼ばれます。クライアント側 のプログラムの例を見ましょう。

        #include        <rpc/rpc.h>



        int     i, o, st;



        st = callrpc( "yaoya", 600000001L, 2L, 1L,

                xdr_int, &i, xdr_int, &o );

        if( st )        clnt_perrno( st );      /* エラーメッセージを出力 */

非常に簡単です。ホスト名yaoyaにあるプログラム番号600000001、バージョン 番号2、手続き番号1の関数を呼び出します。当然ですがyaoyaの上に該当する 番号の関数を持ったサーバプロセスが走っていなければなりません。次の xdr_intは実は関数の名前で、関数への引数を整数としてXDR表現に変換するも のです。次の&iは関数に与えるデータへのポインターです。その次のxdr_int は先程と同じですが、今度は戻り値をXDRから通常の表現に戻す関数として指 定します。最後の&oに結果が返されることになります。このように引数や戻り 値をポインターで渡すことに注意が必要です。

ではサーバプログラムを書きましょう。

        #include        <rpc/rpc.h>



        char *func( int ip )    /* この関数を登録する*/

        int     *ip;

        {

            static      o;    /* 値を返す記憶域としてstatic宣言 */

            o = *ip + 1;        /* 何か計算をする。*/

            return ( char * ) &o;       /* 結果をポインターで返す。*/

        }



        main( )

        {

            int st;

            /* 関数funcをrpcに登録 */

            st = registerrpc( 600000001L, 2L, 1L,

                        func, xdr_int, xdr_int );

            if( st )    {       /* 登録に失敗 */        }

            svc_run( );    /* サーバとして走る。戻ってこない。*/

        }

サーバ側プログラムも非常に簡単ですね。ではサーバを走らせてみましょう。 もちろんya oyaの上でです。果たしてこの関数funcは登録されているでしょう か。シェルからrpcinfoコマンドを使って調べてみます。今、sakanayaにいる とします。

        % rpcinfo -p yaoya

           program vers proto   port

            100000      2     tcp    111  portmapper

            100000      2     udp    111  portmapper

          100029      1     udp    661  keyserv

        ...(途中省略)...

         600000001      2     udp   1080

        %

プログラム番号600000001、バージョン2が登録されているのがわかります。 sakanayaでクライアントプログラムを走らせましょう。今の例ではわざわざ yaoyaまで行って1足して帰ってきました。とてつもなく大きい番号をプログラ ム番号に選んだのは色々なネットワークサービスと競合しないようにしたため です。ユーザは0x20000000から0x3fffffffの間を使うように指示されています。 600000001はこの範囲にはいる数字です。予約されているプログラム番号は /etc/rpcというファイルに書かれています。

実際の関数では引数や戻り値が整数だけということはありません。整数や文 字列や実数の混ざった引数を使った呼出しをしたくなります。この場合ちょっ と複雑です。上の例ではxdr_intという変換ルーチンを使用しました。今度は 引数や戻り値を格納する構造体を用意してそれを変換するXDR関数を書いてや る必要があります。サーバクライアントで共通な構造体と、XDR変換関数を次 のように用意します。

        struct  inarg

        {

            int         i;

            double      d;

        };

        #define MAXSTRSIZE      256

        struct  outarg

        {

            int         i;

            char                *s;

        }

この例では入力用構造体inargは整数と実数、出力用構造体outargは整数と文 字列をメンバーに持っています。これらの構造体用のXDR変換関数は次のよう になります。

        bool_t  xdr_in( xdrsp, argp )

        XDR     *xdrsp;

        struct inarg    *argp;

        {

            if( ! xdr_int( xdrsp, &( argp -> i ) ) ) return FALSE;

            if( ! xdr_double( xdrsp, &( argp -> d ) ) ) return FALSE;

            return TRUE;

        }

        bool_t  xdr_out( xdrsp, argp )

        XDR     *xdrsp;

        struct outarg   *argp;

        {

            if( ! xdr_int( xdrsp, &( argp -> i ) ) ) return FALSE;

            if( ! xdr_string( xdrsp, &( argp -> s ),

                        MAXSTRSIZE ) ) return FALSE;

            return TRUE;

        }

XDR関数の中では構造体メンバーのそれぞれについて整数や実数などのプリミ ティブのXDR関数を呼んでいるだけです。この例で出力用構造体に文字列への ポインターがメンバーに入っています。すこし注意が必要です。ではクライア ントを見ましょう。

        main( )

        {

            int st;

            char        obuf[ MAXSTRSIZE ];

            struct inarg        i;

            struct outarg       o;



            i.i = 123;  /* 整数型引数 */

            i.d = 0.123456;     /* 実数型引数 */

            o.s = obuf;         /* 文字列の値が返る*/

            if( st = callrpc( "yaoya", 600000001L, 2L, 1L,

                xdr_in, &i, xdr_out, &o ) )

            {

                clnt_perrno( st );      /* エラー */

                exit( st );

            }

            printf( "%d %s\n", o.i, o.s );      /* 結果を何かに使う。*/

                ...

入力用データを構造体iで与えていますが、文字列型の結果を受け取るo.sメン バーもあらかじめ初期化しています。サーバ側は、

        struct outarg   *func( ip )    /* これが対象となる関数 */

        struct inarg    *ip;    /* 入力用引数 */

        {

            static      struct outarg   o;

            static      char    obuf[ MAXSTRSIZE ];

            o.i = ip -> i * ip -> i;    /* 引数を使ってなにか演算 */

            sprintf( obuf, "input = %d, %f", ip -> i, ip -> d );

            o.s = obuf;

            return &o;  /* 結果を返す */

        }



        main( )

        {

            int st;

            st = registerrpc( 600000001L, 2L, 1L,

                        func, xdr_in, xdr_out );

            if( st )    {       /* 登録に失敗 */        }

            svc_run( ); /* 無限ループ */

        }

funcのなかで使った構造体や文字列は後からXDR関数で参照されるのでstatic でなければなりません。(もしくはグローバル変数やmallocで与えてももちろ ん結構です。) これで任意の型の引数の組み合わせでrpcを使うことが出来る ようになりました。プリミティブのX DR関数はman xdrで調べてみてください。

rpcinfoで見てわかるようにrpcはtcpやudpプロトコルを使って実装されてい ます。ここで見た例の場合はデフォルトを使っているのでudpプロトコルが使 われます。tcpプロトコルを使うなどもっと細かな設定をしたい場合などのた めに沢山の関数が用意されています。man rpcをやってみてください。



next up previous contents
Next: デーモンプロセス Up: ネットワークプログラムを書く Previous: データグラム型ソケット通信



Kinya Hibino
Sun Jan 14 21:36:40 JST 1996