並行システム
                               システム情報系/情報工学域,
			       システム情報工学研究群/情報理工学位プログラム
			       システム情報工学研究科/コンピュータサイエンス専攻
                               新城 靖
                               <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19
あるいは、次のページから手繰っていくこともできます。
	http://www.cs.tsukuba.ac.jp/~yas/cs/
	http://www.cs.tsukuba.ac.jp/~yas/
遠隔手続き呼び出しは、手続き呼び出しの一種ではない。プロセス間通信の 仕組みの1つ。 An RPC is not a procedure call. An RPC is an interprocess communication mechanism.
Referee: 
Andrew D. Birrell and Bruce Jay Nelson. "Implementing remote procedure
calls", ACM Trans. Comput. Syst. Vol.2, No.1, pp.39-59 (February
1984).  DOI=10.1145/2080.357392 
https://dl.acm.org/doi/10.1145/2080.357392
例(example):手続き put()。ハッシュ表(hash table)にデータを格納する手続
きで、引数(arguments)にキー(key)となる文字列(string)と値(value)となる整
数(integer)を取る。
 図? スタブによるRPCの実現
typedef string key_t<256>; // A string. Its maximum length is 256.
struct keyvalue_t { 
   key_t key; 
   int   value ;
};
typedef key_t  keyarray_t<>; // A variable length array.
program HASHTABLE_PROG { 
   version HASHTABLE_VERSION {
       int        PUT(keyvalue_t)  = 11 ; 
       int        GETVALUE(key_t)  = 12 ; 
       keyarray_t GETKEYS(void)    = 13 ; 
   } = 1 ;
} = 0x20051001 ;
インタフェース定義をスタブ生成器(stub generator)に与えると、クライアン
ト側スタブ(client stub)、サーバ側スタブ(server stub)が自動的に生成され
る。
インタフェース記述の内容 Interface description

図? RPCのバインディング
クライアント側のプログラムとサーバ側のプログラムの対応はもはや1対1で はない。ssize_t read(int fd, void *buf, size_t nbytes)
buf は、結果を受け取る場所を示したもので、RPCで遠隔に送る意味は
ない。
方法1。インタフェースを変える。
struct read_result_t {
       ssize_t read_bytes;
       void *buf;
};
read_result_t read(int fd, size_t nbytes)
方法2: スタブで違いを吸収する。
ssize_t read(int fd, void *buf, size_t nbytes)
{
	fd, buf(nbytes分), nbytes を marshaling する。
	サーバへ要求メッセージとして送る。
	サーバから応答メッセージを受け取る。
	応答メッセージを unmarshaling して、結果の read_bytes と読んだ内容を取り出す。
	読んだ内容を buf read_bytes 分へコピーする。
	return read_bytes ;
}
単純に行えば、上のように余計なコピーが入ることがある。
スタブで最適化すれば、クライアントからサーバへのコピーを減らせる。
この機能を使って送れるもの。 SunRPC can send following data structures.
XML-RPC, SOAP, JSON-RPC など、テキストを使う RPC もある。
rpcgen
というコマンド。
rpcgen コマンドを使うには、次のようなファイルを作成する。
図? rpcgenによるRPCプログラム開発で利用するファイル
開発者は次のようなプログラムを記述する。 A developer writes following programs.name.x
name_client.c
name_server.c
% rpcgen name.x ![[←]](../icons/screen-return.gif) 
次の4つのファイルが生成される。
The rpcgen command generates four files.
name.h
name_clnt.c
name_xdr.c
name.x で定義したデータ構造について、
eXternal Data Representation (XDR)
のための手続き(マーシャリングとアンマーシャリングを行なう手続き) 。
クライアント側とサーバ側の両方で使われる。
Functions that performs marshaling and unmarshaling based on 
eXternal Data Representation (XDR).
These functions are used by both clients and servers.
name_svc.c
/etc/rpc
にある。
SunRPCでは, 最終的にはTCP/IPまたはUDP/IPでデータが送られる。プログラム 番号などTCP/IPまたはUDP/IPのポート番号を対応させる必要がある。
各ホストには
portmap (portmapper)
というサーバがいて、3つ組
<Program number,version number,protocol>を、TCP/IPまたはUDP/IPのポート番号へ変換する。
portmap の情報は、
rpcinfo
 コマンドで表示できる。
% rpcinfo -p ![[←]](../icons/screen-return.gif) program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100007    2   tcp   1024  ypbind
    100007    2   udp   1027  ypbind
    100007    1   tcp   1024  ypbind
    100007    1   udp   1027  ypbind
    100005    1   udp    831  mountd
    100005    2   udp    831  mountd
    100005    1   tcp    834  mountd
    100005    2   tcp    834  mountd
    100003    2   udp   2049  nfs
    100001    2   udp   4193  rstatd
    100001    3   udp   4193  rstatd
    100001    4   udp   4193  rstatd
%
   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100007    2   tcp   1024  ypbind
    100007    2   udp   1027  ypbind
    100007    1   tcp   1024  ypbind
    100007    1   udp   1027  ypbind
    100005    1   udp    831  mountd
    100005    2   udp    831  mountd
    100005    1   tcp    834  mountd
    100005    2   tcp    834  mountd
    100003    2   udp   2049  nfs
    100001    2   udp   4193  rstatd
    100001    3   udp   4193  rstatd
    100001    4   udp   4193  rstatd
% ![[]](../icons/screen-cursor.gif) 
他のホストの情報も調べられる。
% rpcinfo -p hostname ![[←]](../icons/screen-return.gif) 
サーバは、起動時に、portmap に登録する。
bool_t pmap_set(program, version, protocol, port) u_long program; u_long version; int protocol; u_short port;クライアントは、呼び出す前に、ポート番号を調べる。
u_short pmap_getport(address, program, version, protocol) struct sockaddr_in *address; u_long program; u_long version; u_int protocol;
スタブが自動的に呼び出すので、普段は気にすることはない。
portmap 自身も RPC で動いている。portmap の自身のポート番号は、111 と 決まっている。
rpcbind (portmap) が動作しているのに、rpcinfo -p でつながらない時には、 パケットフィルタやファイアウォールでポート番号 111 を止めていないか調べ る。
$ rpcinfo -p ![[←]](../icons/screen-return.gif) Can't contact rpcbind on localhost
rpcinfo: RPC: Remote system error - Connection refused
$
Can't contact rpcbind on localhost
rpcinfo: RPC: Remote system error - Connection refused
$ ![[]](../icons/screen-cursor.gif) 
その時は、root で 次のようにして rpcbind を実行する。
# launchctl start com.apple.rpcbind ![[←]](../icons/screen-return.gif) 
実験が終わったら、root で 次のようにして rpcbind を終了する。
# launchctl stop com.apple.rpcbind ![[←]](../icons/screen-return.gif) 
Linux 等、その他のシステムで、rpcinfo -p localhost がうまく表示できな い時には、次のパッケージを入れてみる。
NFS クライアントでうまくいかない時には、NFS サーバを試す。
void up(void)
int getvalue(void)
void reset(int)
   1: 
   2: /*
   3:         counter-local.c -- Local counter program in C
   4:         Created on: 2012/01/18 22:19:35
   5: */
   6: 
   7: #include <stdio.h>      /* stderr, fprintf() */
   8: 
   9: static int val;
  10: 
  11: void
  12: counter_up(void)
  13: {
  14:         val ++;
  15: }
  16: 
  17: int
  18: counter_getvalue(void)
  19: {
  20:         return( val );
  21: }
  22: 
  23: void
  24: counter_reset(int new_value)
  25: {
  26:         val = new_value;
  27: }
  28: 
  29: int main( int argc, char *argv[], char *envp[] ) {
  30:     int i;
  31:         for( i=0; i<3; i++ )
  32:         {
  33:             counter_up();
  34:             printf("counter value == %d\n",counter_getvalue());
  35:         }
  36: }
実行例
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-local.c ![[←]](../icons/screen-return.gif) % make counter-local
% make counter-local ![[←]](../icons/screen-return.gif) gcc     counter-local.c   -o counter-local
% ./counter-local
gcc     counter-local.c   -o counter-local
% ./counter-local  ![[←]](../icons/screen-return.gif) counter value == 1
counter value == 2
counter value == 3
% ./counter-local
counter value == 1
counter value == 2
counter value == 3
% ./counter-local ![[←]](../icons/screen-return.gif) counter value == 1
counter value == 2
counter value == 3
%
counter value == 1
counter value == 2
counter value == 3
% ![[]](../icons/screen-cursor.gif) 
   1: program COUNTER_PROG { 
   2:    version COUNTER_VERSION {
   3:        void     COUNTER_UP(void)       = 11 ; 
   4:        int      COUNTER_GETVALUE(void) = 12 ; 
   5:        void     COUNTER_RESET(int)     = 13 ; 
   6:    } = 1 ;
   7: } = 0x20052001 ;
COUNTER_UP
COUNTER_GETVALUE
COUNTER_RESET
が手続きの名前(手続き番号を示す定数の定義)。
手続きの定義を program 番号と
version 番号の括弧が取り囲んでいる。
システムによって生成されるものが異なる。 ヘッダファイルの主要部分は、以下の通りである。
... 14: #define COUNTER_PROG ((rpc_uint)0x20052001) 15: #define COUNTER_VERSION ((rpc_uint)1) ... 28: #elif __STDC__ 29: #define COUNTER_UP ((rpc_uint)11) 30: extern void * counter_up_1(void *, CLIENT *); 31: extern void * counter_up_1_svc(void *, struct svc_req *); 32: #define COUNTER_GETVALUE ((rpc_uint)12) 33: extern int * counter_getvalue_1(void *, CLIENT *); 34: extern int * counter_getvalue_1_svc(void *, struct svc_req *); 35: #define COUNTER_RESET ((rpc_uint)13) 36: extern void * counter_reset_1(int *, CLIENT *); 37: extern void * counter_reset_1_svc(int *, struct svc_req *); ...
   1: 
   2: /*
   3:         counter-client.c -- counter client based on SunRPC
   4:         Created on: 2011/12/21 20:56:17
   5: */
   6: 
   7: #include <stdio.h>      /* stderr, fprintf() */
   8: #include <string.h>
   9: #include <stdlib.h>     /* exit() */
  10: #include "counter.h"
  11: 
  12: void usage( char *comname ) {
  13:         fprintf(stderr,"Usage: %% %s server-host\n", comname);
  14:         exit( 1 );
  15: }
  16: 
  17: int main( int argc, char *argv[], char *envp[] ) {
  18:     char *server ;
  19:     CLIENT *clnt;
  20:     char *counter_up_arg; /* dummy */
  21:     void *counter_up_result;
  22:     char *counter_getvalue_arg; /* dummy */
  23:     int  *counter_getvalue_result;
  24:     int i;
  25: 
  26:         if( argc != 2 )
  27:             usage( argv[0] );
  28:         server = argv[1];
  29:         clnt = clnt_create(server,COUNTER_PROG,
  30:                            COUNTER_VERSION, "tcp");
  31:         if( clnt == NULL ) {
  32:             clnt_pcreateerror( server );
  33:             exit( 1 );
  34:         }
  35: 
  36:         for( i=0; i<3; i++ )
  37:         {
  38:             counter_up_result = counter_up_1((void *)&counter_up_arg, clnt);
  39:             if( counter_up_result == NULL ) {
  40:                 clnt_perror(clnt, "call failed -- up");
  41:                 exit( 2 );
  42:             }
  43:             xdr_free( (xdrproc_t)xdr_void, (char *)counter_up_result );
  44: 
  45:             counter_getvalue_result = counter_getvalue_1((void *)&counter_getvalue_arg, clnt);
  46:             if( counter_getvalue_result == (void *) NULL )
  47:             {
  48:                 clnt_perror(clnt, "call failed -- getvalue");
  49:                 exit( 2 );
  50:             }
  51:             printf("value==%d\n", *counter_getvalue_result);
  52:             xdr_free( (xdrproc_t)xdr_int, (char *)counter_up_result );
  53:         }
  54:         clnt_destroy( clnt );
  55: }
counter_up_1(),counter_getvalue_1() を呼び出す部分である。
引数は、インタフェースで定義された型(この例ではダミーの voidへの
ポインタ)と、CLIENT 構造体へのポインタ、結果は、インタ
フェースで定義された型(void, int)へのポインタである。
スタブcounter_up_1(),counter_getvalue_1(),counter_reset_1() は、
rpcgen が生成する*_clnt.c というファイルに含まれてる。こ
れは、引数をマーシャリングして要求メッセージを組み立て、サーバに送信す
る。サーバから応答メッセージを受け取り、アンマーシャリングして、結果と
して返す。アンマーシャリングの時、結果を保持するメモリを、malloc() で確
保している。
clnt_create() は、CLIENT 構造体を確保する。
引数は、サーバのホスト名、
プログラム番号、
バージョン番号、
通信に使うプロトコルである。
使い終わった構造体は、clnt_destroy() で解放する。
結果は、 void* か int*である。
xdr_free() で、アンマーシャリングの時に確保したメモリを解放している。
内部では、free() が呼ばれている。
(int や void を返す手続きでは、実際には static 変数の番地を return 
しているので何もしない。)
   1: 
   2: /*
   3:         counter-server.c -- The counter server using SunRPC.
   4: */
   5: 
   6: #include <stdio.h>      /* stderr, fprintf() */
   7: #include <stdlib.h>     /* strtol() */
   8: #include <string.h>     /* memcpy() */
   9: #include "counter.h"
  10: 
  11: static int val;
  12: 
  13: void *
  14: counter_up_1_svc(void *argp, struct svc_req *rqstp)
  15: {
  16:         static char* result;
  17:         val ++;
  18:         return((void*) &result);
  19: }
  20: 
  21: int *
  22: counter_getvalue_1_svc(void *argp, struct svc_req *rqstp)
  23: {
  24:         static int  result;
  25:         result = val;
  26:         return(&result);
  27: }
  28: 
  29: void *
  30: counter_reset_1_svc(int *argp, struct svc_req *rqstp)
  31: {
  32:         static char* result;
  33:         val = *argp;
  34:         return((void*) &result);
  35: }
サーバ側では、手続き counter_up_1_svc(),counter_getvalue_1_svc(),counter_reset_1_svc() を記述する。
引数と結果は、rpcgen のソースで定義した構造体へのポインタ。
結果を返す時の構造体へのポインタを返す方法が問題。自動変数(auto 変数) にすると、呼出し側に戻った瞬間に無効になる。よく使われるのは、静的変数 (static 変数)に結果を代入して、返すことだが、マルチスレッドプログラミ ングでは問題になる。
サーバ側の main() 関数は、rpcgen により
自動生成される。このmain()では、
ポートマッパ(port mapper)
へのプログラム番号とバージョン番号の登録される。
% mkdir counter-rpc ![[←]](../icons/screen-return.gif) % cd counter-rpc
% cd counter-rpc ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client.c
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client.c ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server.c
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server.c ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.x
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.x ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % ls
% ls ![[←]](../icons/screen-return.gif) Makefile  counter-client.c  counter-server.c  counter.x
% make counter-client
Makefile  counter-client.c  counter-server.c  counter.x
% make counter-client ![[←]](../icons/screen-return.gif) rpcgen counter.x
gcc -g -DRPC_SVC_FG   -c -o counter-client.o counter-client.c
gcc -g -DRPC_SVC_FG   -c -o counter_clnt.o counter_clnt.c
gcc -g -DRPC_SVC_FG  counter-client.o counter_clnt.o  -o counter-client
% make counter-server
rpcgen counter.x
gcc -g -DRPC_SVC_FG   -c -o counter-client.o counter-client.c
gcc -g -DRPC_SVC_FG   -c -o counter_clnt.o counter_clnt.c
gcc -g -DRPC_SVC_FG  counter-client.o counter_clnt.o  -o counter-client
% make counter-server ![[←]](../icons/screen-return.gif) gcc -g -DRPC_SVC_FG   -c -o counter-server.o counter-server.c
gcc -g -DRPC_SVC_FG   -c -o counter_svc.o counter_svc.c
gcc -g -DRPC_SVC_FG counter-server.o counter_svc.o   -o counter-server
% ls
gcc -g -DRPC_SVC_FG   -c -o counter-server.o counter-server.c
gcc -g -DRPC_SVC_FG   -c -o counter_svc.o counter_svc.c
gcc -g -DRPC_SVC_FG counter-server.o counter_svc.o   -o counter-server
% ls ![[←]](../icons/screen-return.gif) Makefile          counter-server    counter.x       counter_svc.o
counter-client    counter-server.c  counter_clnt.c
counter-client.c  counter-server.o  counter_clnt.o
counter-client.o  counter.h         counter_svc.c
%
Makefile          counter-server    counter.x       counter_svc.o
counter-client    counter-server.c  counter_clnt.c
counter-client.c  counter-server.o  counter_clnt.o
counter-client.o  counter.h         counter_svc.c
% ![[]](../icons/screen-cursor.gif) 
この例では、rpcgen では 3 つのファイル
counter.h counter_clnt.c counter_svc.c が作られる。
Linux Ubuntu で rpcgen コマンドがない、rpc.h が見付からない時には、次 のようなパッケージを入れる。
#CFLAGS = -g -DRPC_SVC_FG -I/usr/include/tirpc #rpc_libs = -ltirpcclnt_create が見付からないと言われたら Makefile にある次の行をコメントアウトする。
#CFLAGS = -g -DRPC_SVC_FG -I/usr/include/tirpc #rpc_libs = -ltirpc
macOS 等で rpcgen が生成したファイル counter_clnt.c がコンパイルできない時には、 Makefile にある次の行をコメントアウトする。
#CFLAGS = -g -DRPC_SVC_FG -Wno-error=implicit-function-declaration -Wno-incompatible-pointer-types -Wno-implicit-function-declaration
例題を実行するには、クライアント用とサーバ用に端末を2つ開く。
その方法は、make help で表示される。
クライアントとサーバは、別のコンピュータで動作させても良い。 以下の例では、同じコンピュータで動かし、 クライアント側では localhost を指定して実行している。
サーバの実行。Run the server.
% ./counter-server  ![[←]](../icons/screen-return.gif) 
サーバは終了しないので、実験が終わったら ^C で止める。
クライアント側は、もう1つの端末で実行する。
rpcinfo コマンドで、サーバのプログラム番号、プロトコルポート番号
を確認する。
% rpcinfo -p localhost ![[←]](../icons/screen-return.gif) program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100024    1   udp    977  status
    100024    1   tcp    980  status
    100021    1   udp  36430  nlockmgr
    100021    3   udp  36430  nlockmgr
    100021    4   udp  36430  nlockmgr
    100021    1   tcp  35786  nlockmgr
    100021    3   tcp  35786  nlockmgr
    100021    4   tcp  35786  nlockmgr
 537206785    1   udp  45125
 537206785    1   tcp  52142
%
   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100024    1   udp    977  status
    100024    1   tcp    980  status
    100021    1   udp  36430  nlockmgr
    100021    3   udp  36430  nlockmgr
    100021    4   udp  36430  nlockmgr
    100021    1   tcp  35786  nlockmgr
    100021    3   tcp  35786  nlockmgr
    100021    4   tcp  35786  nlockmgr
 537206785    1   udp  45125
 537206785    1   tcp  52142
% ![[]](../icons/screen-cursor.gif) 
537206785 は、16進数で0x20052001。
クライアントを実行する。Run the clietn。
% ./counter-client  ![[←]](../icons/screen-return.gif) Usage: % ./counter-client server-host
% ./counter-client localhost
Usage: % ./counter-client server-host
% ./counter-client localhost ![[←]](../icons/screen-return.gif) value==1
value==2
value==3
% ./counter-client localhost
value==1
value==2
value==3
% ./counter-client localhost ![[←]](../icons/screen-return.gif) value==4
value==5
value==6
% ./counter-client localhost
value==4
value==5
value==6
% ./counter-client localhost ![[←]](../icons/screen-return.gif) value==7
value==8
value==9
%
value==7
value==8
value==9
% ![[]](../icons/screen-cursor.gif) 
実験が終了したら、サーバを ^C コマンドで削除する。
% ./counter-server  ![[←]](../icons/screen-return.gif) ^C
%
^C
% ![[]](../icons/screen-cursor.gif) 
マーシャリングには、SunRPC と同様に、ポインタの先を送る事ができる。リ スト構造や木構造が扱える。ただし、SunRPC とは違い、自分でマーシャリン グのための手続きを定義しなければならない。
例: マーシャリングのための手続き hg_proc_int_list_t() の手書き。 Serializing complex data structures
SunRPC なら、次のように書くと、マーシャリングのための関数 xdr_int_list() が自動生成される。
   1: struct int_list {
   2:   int              value;
   3:   struct int_list *next;
   4: };
rpcgen で自動生成されたマーシャリングのための関数。
   1: /*
   2:  * Please do not edit this file.
   3:  * It was generated using rpcgen.
   4:  */
   5: 
   6: #include "int_list.h"
   7: 
   8: bool_t
   9: xdr_int_list(xdrs, objp)
  10:         XDR *xdrs;
  11:         int_list *objp;
  12: {
  13: 
  14:         if (!xdr_int(xdrs, &objp->value))
  15:                 return (FALSE);
  16:         if (!xdr_pointer(xdrs, (char **)&objp->next, sizeof(int_list), (xdrproc_t)xdr_int_list))
  17:                 return (FALSE);
  18:         return (TRUE);
  19: }

図? RPCで起こりうる障害
例: 銀行預金転送。
対策:
RPCのセマンティクス
サーバにあり、親(クライアント)がいない計算を孤児という。
対応方法
孤児が、ロックを持っていたら、孤児を消しただけでは話を終わらない。
Linux, /usr/include/rpc/clnt.h
/*
 * Rpc calls return an enum clnt_stat.  This should be looked at more,
 * since each implementation is required to live with this (implementation
 * independent) list of errors.
 */
enum clnt_stat {
        RPC_SUCCESS=0,                  /* call succeeded */
        /*
         * local errors
         */
        RPC_CANTENCODEARGS=1,           /* can't encode arguments */
        RPC_CANTDECODERES=2,            /* can't decode results */
        RPC_CANTSEND=3,                 /* failure in sending call */
        RPC_CANTRECV=4,                 /* failure in receiving result */
        RPC_TIMEDOUT=5,                 /* call timed out */
        /*
         * remote errors
         */
        RPC_VERSMISMATCH=6,             /* rpc versions not compatible */
        RPC_AUTHERROR=7,                /* authentication error */
        RPC_PROGUNAVAIL=8,              /* program not available */
        RPC_PROGVERSMISMATCH=9,         /* program version mismatched */
        RPC_PROCUNAVAIL=10,             /* procedure unavailable */
        RPC_CANTDECODEARGS=11,          /* decode arguments error */
        RPC_SYSTEMERROR=12,             /* generic "other problem" */
        RPC_NOBROADCAST = 21,           /* Broadcasting not supported */
        /*
         * callrpc & clnt_create errors
         */
        RPC_UNKNOWNHOST=13,             /* unknown host name */
        RPC_UNKNOWNPROTO=17,            /* unknown protocol */
        RPC_UNKNOWNADDR = 19,           /* Remote address unknown */
        /*
         * rpcbind errors
         */
        RPC_RPCBFAILURE=14,             /* portmapper failed in its call */
#define RPC_PMAPFAILURE RPC_RPCBFAILURE
        RPC_PROGNOTREGISTERED=15,       /* remote program is not registered */
        RPC_N2AXLATEFAILURE = 22,       /* Name to addr translation failed */
        /*
         * unspecified error
         */
        RPC_FAILED=16,
        RPC_INTR=18,
        RPC_TLIERROR=20,
        RPC_UDERROR=23,
        /*
         * asynchronous errors
         */
        RPC_INPROGRESS = 24,
        RPC_STALERACHANDLE = 25
};
例:
無状態サーバ(stateless server) とは、サーバ内部に状態を保持しないようなサーバ。
状態の例
RPCで冪等な操作や無状態サーバを実現すると、クラッシュに強いシステムを 作れる。クライアントは、サーバから応答がない場合、何度要求を再送信して もよい。
例:NFS Version 2
サーバは、ファイルに対する書き込み要求を受け付けると、ディスクへ の書き込みを完了してから応答を返す。 応答が返ってきた要求は、 ディスクへの書き込みが完了したことが保証されている。 この段階でサーバがクラッシュしたとしても、なにも失われない。
しかし、、、重たい。NFS Version 3 では、状態付きのサーバになった。
NFS ( Network File System ) は, Sun Microsystems 社が 開発したネットワーク・ファイル・システムの名前(固有名詞, 商標)。
その他のネットワーク・ファイル・システム(用のプロトコル)
NFSを使うと, ネットワークを通じて別のコンピュータ上のファイルシステム
の一部分を, ローカルディスク上にあるファイルシステムと同じように, 自分
のファイルシステムの木に
マウント(mount)
できる。
 図? NFSによるファイルの共有
相互に参照し合える。
 表? NFSで使われているRPCの手続き
| 手続き名 | 意味 | 関連するコマンド、 システムコール | 
|---|---|---|
| null() | 何もしない | rpcinfo -u hostname nfsコマンド | 
| getattr() | 属性の読み出し | ls -lコマンド,statシステムコール ,openシステムコール | 
| setattr() | 属性の設定 | chmod,chownコマンド | 
| lookup() | ファイルの検索 | openシステムコール | 
| readlink() | シンボリックリンクの読み出し | ls -lコマンド,readlinkシステムコール | 
| read() | ファイルの読み出し | readシステムコール | 
| write() | ファイルの書き込み | writeシステムコール | 
| create() | ファイルの作成 | creatシステムコール,openシステムコール | 
| remove() | ハードリンクの削除 | rmコマンド,unlinkシステムコール | 
| rename() | ファイル名前の変更 | mvコマンド,renameシステムコール | 
| link() | ハードリンクの作成 | lnコマンド,linkシステムコール | 
| symlink() | シンボリックリンクの作成 | ln -sコマンド,symlinkシステムコール | 
| mkdir() | ディレクトリの作成 | mkdirコマンド | 
| rmdir() | ディレクトリの削除 | rmdirコマンド | 
| readdir() | ディレクトリの読み出し | lsコマンド | 
| statfs() | ファイルシステムの利用状況 | dfコマンド,statfsシステムコール | 
| commit()* | ディスクへの書き込み | fsyncシステムコール | 
| access()* | アクセス権のチェック | accessシステムコール | 
| open()** | ファイルを開く。 | |
| close()** | ファイルを閉じる。 | |
| lock()** | ファイルのロック。 | |
| renew()** | ファイルのロックの更新。 | |
| compound()** | 複合手続き。複数の手続きをまとめて実行する。 | 
* は、NFS Version 3 で追加された手続き。
** は、NFS v4 で追加された手続き。
NFS でファイルやディレクトリを区別するための識別子。32バイト。
const NFS_FHSIZE	= 32;
...
/*
 * File access handle
 */
struct nfs_fh {
	opaque data[NFS_FHSIZE];
};
opaque (不定形)は、バイト配列の意味。
固定長のデータがそのままネットワークを流れる。
SunRPC では、char data[NFS_FHSIZE] と書くと、本当に文字の配列の意味に
なり、文字単位でマーシャリングが行われる。
nfs_fh の内容は、サーバが独自に(勝手に)決める。
一番最初のNFSファイル・ハンドルをどうやって入手するか。
| 手続き名 | 意味 | 関連するコマンド、 システムコール | 
|---|---|---|
| null() | 何もしない | rpcinfo -u hostname mountコマンド | 
| mnt() | NFSファイルハンドルを返す | mountコマンド | 
| dump() | マウント一覧表 | showmount hostnameコマンド | 
| umnt() | アンマウント | umountコマンド | 
| umntall() | 全アンマウント | umount -h hostnameコマンド | 
| export() | アクセス可能なディレクトリのリストを返す | 
2.2.5.  Look Up File Name
	diropres
	NFSPROC_LOOKUP(diropargs) = 4;
If the reply "status" is NFS_OK, then the reply "file" and reply
"attributes" are the file handle and attributes for the file "name"
in the directory given by "dir" in the argument.
2.3.10.  diropargs
    struct diropargs {
	fhandle  dir;
	filename name;
    };
The "diropargs" structure is used in directory operations.  The
"fhandle" "dir" is the directory in which to find the file "name".
A directory operation is one in which the directory is affected.
2.3.11.  diropres
    union diropres switch (stat status) {
    case NFS_OK:
	struct {
	    fhandle file;
	    fattr   attributes;
	} diropok;
    default:
	void;
    };
The results of a directory operation are returned in a "diropres"
structure.  If the call succeeded, a new file handle "file" and
the "attributes" associated with that file are returned along with
the "status".
2.2.7.  Read From File
	struct readargs {
		fhandle file;
		unsigned offset;
		unsigned count;
		unsigned totalcount;
	};
	union readres switch (stat status) {
	case NFS_OK:
		fattr attributes;
		nfsdata data;
	default:
		void;
	};
	readres
	NFSPROC_READ(readargs) = 6;
Returns up to "count" bytes of "data" from the file given by "file",
starting at "offset" bytes from the beginning of the file.  The first
byte of the file is at offset zero.  The file attributes after the
read takes place are returned in "attributes".
Notes:  The argument "totalcount" is unused, and is removed in the
next protocol revision.
2.2.9.  Write to File
	struct writeargs {
		fhandle file;
		unsigned beginoffset;
		unsigned offset;
		unsigned totalcount;
		nfsdata data;
	};
	attrstat
	NFSPROC_WRITE(writeargs) = 8;
Writes "data" beginning "offset" bytes from the beginning of "file".
The first byte of the file is at offset zero.  If the reply "status"
is NFS_OK, then the reply "attributes" contains the attributes of the
file after the write has completed.  The write operation is atomic.
Data from this "WRITE" will not be mixed with data from another
client's "WRITE".
Notes:  The arguments "beginoffset" and "totalcount" are ignored and
are removed in the next protocol revision.
RPC のように結合(connection)が作られない通信サービスを使う時に冪等や無状態 といった性質を実現する時に必要になる技術。
例:NFSでのディレクトリの読み込み手続き nfsproc_readdir() で、1回の RPC で全部のデータを返せないことが起きる。 ディレクトリのどの位置まで読み込んだかを 示す中間状態を クッキー(cookie) という形でクライアントに返す。
クライアントは、次の RPC の呼び出しで、 前回受けとった応答の中のクッキーを、サーバへの要求に含めて送す。
const NFS_COOKIESIZE = 4; typedef opaque nfscookie[NFS_COOKIESIZE];
2.2.17.  Read From Directory
	 struct readdirargs {
		 fhandle dir;
		 nfscookie cookie;
		 unsigned count;
	 };
	 struct entry {
		 unsigned fileid;
		 filename name;
		 nfscookie cookie;
		 entry *nextentry;
	 };
	 union readdirres switch (stat status) {
	 case NFS_OK:
		 struct {
			 entry *entries;
			 bool eof;
		 } readdirok;
	 default:
		 void;
	 };
	 readdirres
	 NFSPROC_READDIR (readdirargs) = 16;
 Returns a variable number of directory entries, with a total size of
 up to "count" bytes, from the directory given by "dir".  If the
 returned value of "status" is NFS_OK, then it is followed by a
 variable number of "entry"s.  Each "entry" contains a "fileid" which
 consists of a unique number to identify the file within a filesystem,
 the "name" of the file, and a "cookie" which is an opaque pointer to
 the next entry in the directory.  The cookie is used in the next
 READDIR call to get more entries starting at a given point in the
 directory.  The special cookie zero (all bits zero) can be used to
 get the entries starting at the beginning of the directory.  The
 "fileid" field should be the same number as the "fileid" in the the
 attributes of the file.  (See section "2.3.5. fattr" under "Basic
 Data Types".)  The "eof" flag has a value of TRUE if there are no
 more entries in the directory.
nfsproc_readdir() で、1回目と2回目の RPC の間にディレクトリの内容が 更新された場合、どのような結果になるのか不明。
NFS非同期入出力デーモン
(
nfsiod (local NFS asynchronous I/O Daemon)
または
biod (asynchronous Block I/O Daemon)
)
は、NFSのクライアントホスト上で動き、NFSの非同期的な入出力を行う。
次のような障害に対応することが簡単ではない。
クライアント側の lockd
commit() という手続きが追加。
それまでに行われた書き込みをディスクに行うように指示できる。
(NFS v2 では、write で必ずディスクに書き込むか、停電でも保持されるメモリに書き込む。)
XML は、World Wide Web (WWW) で使われている HTML (Hyper Text Markup Language) と同様に、マークアップ言語(markup language)の一種。
マークアップ言語とは、文書(テキスト)に、「ここは表題」、「ここは箇条 書」といった、文書の構造を示す目印(マーク)を付ける機能を持つ言語。
World Wide Web コンソーシアムのXMLのページ
https://www.w3.org/XML/
The term "Web service" is derived from "Web of services". In Web services, software components use other software components over the network by using XML and the World Wide Web technology.
Web service を、一般の World Wide Web と紛れないように呼ぶ時には、 「XML Web Service」と言う。
XML Web Servicesでは、異なるプログラミング言語で書かれたコンポーネントも相 互に接続できる。
WSDL で記述するもの
We can generate client stubs and server stubs from WSDL.
1: <?xml version='1.0' encoding='UTF-8'?> 2: 3: <!--generated by GLUE--> 4: 5: <definitions name='Counter' 6: targetNamespace='http://www.themindelectric.com/wsdl/Counter/' 7: xmlns:tns='http://www.themindelectric.com/wsdl/Counter/' 8: xmlns:electric='http://www.themindelectric.com/' 9: xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' 10: xmlns:http='http://schemas.xmlsoap.org/wsdl/http/' 11: xmlns:mime='http://schemas.xmlsoap.org/wsdl/mime/' 12: xmlns:xsd='http://www.w3.org/2001/XMLSchema' 13: xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/' 14: xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/' 15: xmlns='http://schemas.xmlsoap.org/wsdl/'> 16: 17: <message name='getValue0SoapIn'/> 18: <message name='getValue0SoapOut'> 19: <part name='Result' type='xsd:int'/> 20: </message> 21: <message name='reset1SoapIn'> 22: <part name='newVal' type='xsd:int'/> 23: </message> 24: <message name='reset1SoapOut'/> 25: <message name='up2SoapIn'/> 26: <message name='up2SoapOut'/> 27: 28: <portType name='CounterSoap'> 29: <operation name='getValue'> 30: <input name='getValue0SoapIn' message='tns:getValue0SoapIn'/> 31: <output name='getValue0SoapOut' message='tns:getValue0SoapOut'/> 32: </operation> 33: <operation name='reset' parameterOrder='newVal'> 34: <input name='reset1SoapIn' message='tns:reset1SoapIn'/> 35: <output name='reset1SoapOut' message='tns:reset1SoapOut'/> 36: </operation> 37: <operation name='up'> 38: <input name='up2SoapIn' message='tns:up2SoapIn'/> 39: <output name='up2SoapOut' message='tns:up2SoapOut'/> 40: </operation> 41: </portType> 42: 43: <binding name='CounterSoap' type='tns:CounterSoap'> 44: <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/> 45: <operation name='getValue'> 46: <soap:operation soapAction='getValue' style='rpc'/> 47: <input name='getValue0SoapIn'> 48: <soap:body use='encoded' namespace='http://tempuri.org/Counter' 49: encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/> 50: </input> 51: <output name='getValue0SoapOut'> 52: <soap:body use='encoded' namespace='http://tempuri.org/Counter' 53: encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/> 54: </output> 55: </operation> 56: <operation name='reset'> 57: <soap:operation soapAction='reset' style='rpc'/> 58: <input name='reset1SoapIn'> 59: <soap:body use='encoded' namespace='http://tempuri.org/Counter' 60: encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/> 61: </input> 62: <output name='reset1SoapOut'> 63: <soap:body use='encoded' namespace='http://tempuri.org/Counter' 64: encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/> 65: </output> 66: </operation> 67: <operation name='up'> 68: <soap:operation soapAction='up' style='rpc'/> 69: <input name='up2SoapIn'> 70: <soap:body use='encoded' namespace='http://tempuri.org/Counter' 71: encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/> 72: </input> 73: <output name='up2SoapOut'> 74: <soap:body use='encoded' namespace='http://tempuri.org/Counter' 75: encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/> 76: </output> 77: </operation> 78: </binding> 79: 80: <service name='Counter'> 81: <documentation> 82: // ICounter.java//</documentation> 83: <port name='CounterSoap' binding='tns:CounterSoap'> 84: <soap:address location='http://127.0.0.1:4031/soap/Counter/c1'/> 85: </port> 86: </service> 87: </definitions>
HTTP サーバを内蔵しているので、手軽に試すのに適している。 WSDL を自動生成する機能がある。
The Mind Electric社は、2003年にwebMethods社により買収された。 webMethods社は、2007年に Software AG社によって買収された。
   1: //
   2: // ICounter.java
   3: //
   4: 
   5: public interface ICounter
   6: {
   7:     void up();
   8:     int getValue();
   9:     void reset(int newVal);
  10: };
単に利用するだけならば、インタフェースを定義しなくてもよいが、リモート
との比較のためにあえて定義する。カウンタは、3つの手続きを持つものであ
る。
   1: //
   2: // Counter.java
   3: //
   4: public class Counter implements ICounter
   5: {
   6:     int val;
   7:     public Counter(int initVal)
   8:     {
   9:         val = initVal ;
  10:     }
  11:     public void up()
  12:     {
  13:         val ++ ;
  14:     }
  15:     public int getValue()
  16:     {
  17:         return( val );
  18:     }
  19:     public void reset(int newVal)
  20:     {
  21:         val = newVal ;
  22:     }
  23: };
3つの手続きとコンストラクタを実現している。
//
// CounterLocal.java
//
class CounterLocal
{
    public static void main(String argv[])
    {
	ICounter c1 = new Counter( 10 );
	for( int i=0 ; i<3 ; i++ )
	{
	    c1.up();
	    System.out.println("c1.value=="+c1.getValue());
	}
    }
};
実行例:
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/ICounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/ICounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Counter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Counter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/CounterLocal.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/CounterLocal.java ![[←]](../icons/screen-return.gif) % make CounterLocal
% make CounterLocal ![[←]](../icons/screen-return.gif) javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar ICounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar Counter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterLocal.java
% make run-CounterLocal
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar ICounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar Counter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterLocal.java
% make run-CounterLocal ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterLocal
c1.value==11
c1.value==12
c1.value==13
% make run-CounterLocal
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterLocal
c1.value==11
c1.value==12
c1.value==13
% make run-CounterLocal ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterLocal
c1.value==11
c1.value==12
c1.value==13
%
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterLocal
c1.value==11
c1.value==12
c1.value==13
% ![[]](../icons/screen-cursor.gif) 
オブジェクト c1 を、main で作って実行している。実行する度に新しいオブ ジェクトが new される。
   1: 
   2: import electric.registry.Registry;
   3: import electric.server.http.HTTP;
   4: 
   5: public class CounterServer
   6: {
   7:     public static void main( String[] args ) throws Exception
   8:     {
   9:         if( args.length != 2 )
  10:         {
  11:             System.err.println("Usge: %% java CounterServer portno name");
  12:             System.exit( 1 );
  13:         }
  14:         String portno = args[0];
  15:         String name = args[1];
  16:         String url = "http://localhost:" + portno + "/soap" ;
  17:         HTTP.startup( url );
  18: 
  19:         Registry.publish( name, new Counter(10) );
  20:         System.out.println("Ok.");
  21:     }
  22: }
このプログラムは、2つの引数を取る。
HTTP.Startup() で、Glue に含まれている XML Web Servicesのサーバを起動して いる。その結、果新しいスレッドが作られるので、このプログラムは、main() が終了しても、動き続けることになる。
Registry.publish() は、Glue に含まれている機能である。 これは、与えられた名前で、オブジェクトをアクセス可能にする。
   1: 
   2: import electric.registry.Registry;
   3: 
   4: public class CounterClient
   5: {
   6:     public static void main( String[] args ) throws Exception
   7:     {
   8:         if( args.length != 3 )
   9:         {
  10:             System.err.println("Usge: %% java CounterClient hostname portno name");
  11:             System.exit( 1 );
  12:         }
  13:         String hostname = args[0];
  14:         String portno = args[1];
  15:         String name = args[2];
  16:         String url = "http://" + hostname + ":" + portno + "/soap/" + name + ".wsdl";
  17:         System.out.println("url is " + url );
  18:         ICounter c1 = (ICounter) Registry.bind( url, ICounter.class );
  19:         for( int i=0 ; i<3 ; i++ )
  20:         {
  21:             c1.up();
  22:             System.out.println("c1.value=="+c1.getValue());
  23:         }
  24:     }
  25: }
このプログラムは、3つの引数を取る。
サーバは、は自動的に終了しないので、実験が終わったら ^C (Control-C)で 殺す。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/ICounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/ICounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Counter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Counter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/CounterServer.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/CounterServer.java ![[←]](../icons/screen-return.gif) % emacs Makefile (環境に合わせて Makefile の修正)
% emacs Makefile (環境に合わせて Makefile の修正) ![[←]](../icons/screen-return.gif) % make CounterServer
% make CounterServer ![[←]](../icons/screen-return.gif) javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar ICounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar Counter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer.java
% make run-CounterServer
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar ICounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar Counter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer.java
% make run-CounterServer ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer 8080 Counter/c1
GLUE 1.2 (c) 2001 The Mind Electric
startup server on http://127.0.0.1:8080/soap
Ok.
(最後に ^C で止める)
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer 8080 Counter/c1
GLUE 1.2 (c) 2001 The Mind Electric
startup server on http://127.0.0.1:8080/soap
Ok.
(最後に ^C で止める)
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/ICounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/ICounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/CounterClient.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/CounterClient.java ![[←]](../icons/screen-return.gif) % emacs Makefile (環境に合わせて Makefile の修正)
% emacs Makefile (環境に合わせて Makefile の修正) ![[←]](../icons/screen-return.gif) % make CounterClient
% make CounterClient ![[←]](../icons/screen-return.gif) javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar ICounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterClient.java
% make run-CounterClient
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar ICounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterClient.java
% make run-CounterClient ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterClient localhost 8080 Counter/c1
url is http://localhost:8080/soap/Counter/c1.wsdl
c1.value==11
c1.value==12
c1.value==13
% make run-CounterClient
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterClient localhost 8080 Counter/c1
url is http://localhost:8080/soap/Counter/c1.wsdl
c1.value==11
c1.value==12
c1.value==13
% make run-CounterClient ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterClient localhost 8080 Counter/c1
url is http://localhost:8080/soap/Counter/c1.wsdl
c1.value==14
c1.value==15
c1.value==16
%
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterClient localhost 8080 Counter/c1
url is http://localhost:8080/soap/Counter/c1.wsdl
c1.value==14
c1.value==15
c1.value==16
% ![[]](../icons/screen-cursor.gif) 
クライアントのプログラムを2回実行しても、同じカウンタが使われている所
に注意する。
実際に XML Web Servicesを利用する場合には、いくつかの方法が選べる。
% make run-CounterServer ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer 8080 Counter/c1
Exception in thread "main" java.net.BindException: Address already in use
        at java.net.PlainSocketImpl.socketBind(Native Method)
        at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:331)
        at java.net.ServerSocket.bind(ServerSocket.java:309)
        at java.net.ServerSocket.
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer 8080 Counter/c1
Exception in thread "main" java.net.BindException: Address already in use
        at java.net.PlainSocketImpl.socketBind(Native Method)
        at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:331)
        at java.net.ServerSocket.bind(ServerSocket.java:309)
        at java.net.ServerSocket.(ServerSocket.java:183)
        at java.net.ServerSocket.(ServerSocket.java:139)
        at javax.net.DefaultServerSocketFactory.createServerSocket(DashoA6275)
        at electric.net.socket.tcp.TCPSocketFactory.createServerSocket(Unknown Source)
        at electric.net.socket.SocketServer.createServerSocket(Unknown Source)
        at electric.net.socket.SocketServer.startup(Unknown Source)
        at electric.net.http.WebServer.startup(Unknown Source)
        at electric.net.http.WebServer.startWebServer(Unknown Source)
        at electric.server.http.HTTP.startup(Unknown Source)
        at electric.server.http.HTTP.startup(Unknown Source)
        at electric.server.http.HTTP.startup(Unknown Source)
        at CounterServer.main(CounterServer.java:17)
make: *** [run-CounterServer] Error 1
% ![[]](../icons/screen-cursor.gif) 
  
同じポート番号が使われていた時、Address already in use というエラーが 出る。その時には、まず、自分のプログラム(の残がい)がどこかで動いてい ないかを調べる。他の同じ演習をしている人のプログラムの残がいを見つけた 時には、残がいだったら殺してもらう。本当に別のプログラムに使われていた 時には、別のコンピュータに移動するか、以下のように別のポート番号を指定 して走らせる。
% make run-CounterServer port=1231 ![[←]](../icons/screen-return.gif) java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer 1231 Counter/c1
GLUE 1.2 (c) 2001 The Mind Electric
startup server on http://127.0.0.1:1231/soap
Ok.
(最後に ^C で止める)
java -classpath .:/略/GLUE-STD.jar:./略/servlet.jar CounterServer 1231 Counter/c1
GLUE 1.2 (c) 2001 The Mind Electric
startup server on http://127.0.0.1:1231/soap
Ok.
(最後に ^C で止める)
% telnet 127.0.0.1 8080 ![[←]](../icons/screen-return.gif) Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
GET /soap/Counter/c1.wsdl HTTP/1.0
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
GET /soap/Counter/c1.wsdl HTTP/1.0![[←]](../icons/screen-return.gif) 
![[←]](../icons/screen-return.gif) HTTP/1.1 200 OK
Content-Type: text/xml
Server: GLUE/1.2
Content-Length: 2959
<?xml version='1.0' encoding='UTF-8'?>
<!--generated by GLUE-->
<definitions name='Counter' ... > ...
</definitions>
Connection closed by foreign host.
%
HTTP/1.1 200 OK
Content-Type: text/xml
Server: GLUE/1.2
Content-Length: 2959
<?xml version='1.0' encoding='UTF-8'?>
<!--generated by GLUE-->
<definitions name='Counter' ... > ...
</definitions>
Connection closed by foreign host.
% ![[]](../icons/screen-cursor.gif) 
得られた WSDLの例。
XML Web Servicesでは、World Wide Web の技術を使ってアクセス制御を行うことができる。
別の Java 仮想計算機間オブジェクトのメソッドを呼び出す仕組み。 RMI は、いくつかの層を見えなくする。
比較的軽い。組込みシステムで使われているような遅めのコンピュータに向い ている。
src/java/rmi/Remote.java
public interface Remote {}
これを見つけると、コンパイラが特殊なコードを生成する。
java.lang.Object (class)
  |
  +--java.rmi.server.RemoteObject (class)
        |
        +--java.rmi.server.RemoteServer (class)
              |
              +--java.rmi.server.UnicastRemoteObject (class)
これに加えて、interface Remote を implements する。
クライアント側は、これに比べて簡単。違いは、サーバに接続する部分部分と、 分散固有の例外を受ける部分。
rmiregistry は、デフォルトでポート番号 1099 を使う。 複数人が1つのホストを使うとぶつかる。
   1: //
   2: // IRCounter.java
   3: //
   4: 
   5: public interface IRCounter extends java.rmi.Remote
   6: {
   7:     public void up() throws java.rmi.RemoteException;
   8:     public int  getValue() throws java.rmi.RemoteException;
   9:     public void reset(int newVal) throws java.rmi.RemoteException;
  10: }
リモート・インタフェースの特徴 characteristic of remote interfaces:
   1: //
   2: // RCounter.java
   3: //
   4: 
   5: import java.rmi.*;
   6: import java.rmi.server.*;
   7: 
   8: public class RCounter extends java.rmi.server.UnicastRemoteObject
   9:     implements IRCounter
  10: {
  11:     int val;
  12:     public RCounter(int initVal) throws RemoteException
  13:     {
  14:         super();
  15:         val = initVal ;
  16:     }
  17:     public void up() throws java.rmi.RemoteException
  18:     {
  19:         val ++ ;
  20:     }
  21:     public int getValue() throws java.rmi.RemoteException
  22:     {
  23:         return( val );
  24:     }
  25:     public void reset(int newVal) throws java.rmi.RemoteException
  26:     {
  27:         val = newVal ;
  28:     }
  29: };
  30: 
オブジェクトは、serialize (直列化) されてコピーで渡される。
   1: //
   2: // RCounterServer.java
   3: //
   4: 
   5: import java.rmi.registry.Registry;
   6: import java.rmi.registry.LocateRegistry;
   7: import java.rmi.RemoteException;
   8: import java.rmi.server.UnicastRemoteObject;
   9: 
  10: public class RCounterServer
  11: {
  12:     public static void main(String argv[]) throws Exception
  13:     {
  14:         if( argv.length != 1 )
  15:         {
  16:             System.err.println("Usage% java RCounterServer rmiregistry-portno");
  17:             System.exit( 1 );
  18:         }
  19:         String rmiregport_s = argv[0];
  20:         int rmiregport = Integer.parseInt( rmiregport_s );
  21:         Registry registry = LocateRegistry.getRegistry( rmiregport );
  22: 
  23:         IRCounter c1 = new RCounter( 10 );
  24:         String name = "/Counter/c1" ;
  25:         registry.rebind( name, c1 );
  26:     }
  27: };
Exception を catch したら、最後に System.exit( 1 ) した方がよい。
   1: //
   2: // RCounterClient.java
   3: //
   4: 
   5: import java.rmi.registry.LocateRegistry;
   6: import java.rmi.registry.Registry;
   7: 
   8: class RCounterClient
   9: {
  10:     public static void main(String argv[]) throws Exception
  11:     {
  12:         if( argv.length != 2 )
  13:         {
  14:             System.err.println("Usage% java RCounterClient hostname rmiregistry-portno");
  15:             System.exit( 1 );
  16:         }
  17:         String hostname = argv[0];
  18:         String rmiregport_s = argv[1];
  19:         int rmiregport = Integer.parseInt( rmiregport_s );
  20:         Registry registry = LocateRegistry.getRegistry( hostname, rmiregport );
  21: 
  22:         String name = "/Counter/c1";
  23:         IRCounter c1 = (IRCounter) registry.lookup( name );
  24:         for( int i=0 ; i<3 ; i++ )
  25:         {
  26:             c1.up();
  27:             System.out.println("c1.value=="+c1.getValue());
  28:         }
  29:     }
  30: };
一方のウインドウで rmiregistry を起動する。ポート番号は、ぶつからない ように uid を使う。rmiregistry は自動的に終了しないので、実験が終わっ たら ^C (Control-C)で殺す。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/IRCounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/IRCounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounter.java ![[←]](../icons/screen-return.gif) % make run-rmiregistry
% make run-rmiregistry ![[←]](../icons/screen-return.gif) rmiregistry 8080
(最後に ^C で止める)
rmiregistry 8080
(最後に ^C で止める)
一方のウインドウでサーバを動作させる。サーバもは自動的に終了しないので、
実験が終わったら ^C (Control-C)で殺す。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/IRCounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/IRCounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounterServer.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounterServer.java ![[←]](../icons/screen-return.gif) % make RCounterServer
% make RCounterServer ![[←]](../icons/screen-return.gif) javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar IRCounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar RCounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar RCounterServer.java
rmic RCounter
% make run-RCounterServer
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar IRCounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar RCounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar RCounterServer.java
rmic RCounter
% make run-RCounterServer ![[←]](../icons/screen-return.gif) java -Djava.rmi.server.hostname=127.0.0.1 RCounterServer 8080
(最後に ^C で止める)
java -Djava.rmi.server.hostname=127.0.0.1 RCounterServer 8080
(最後に ^C で止める)
Java RMI ConnectException: Connection refused to host: 127.0.0.1 エラーの正しい理解と対策 参照。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/IRCounter.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/IRCounter.java ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounterClient.java
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/RCounterClient.java ![[←]](../icons/screen-return.gif) % make RCounterClient
% make RCounterClient ![[←]](../icons/screen-return.gif) javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar IRCounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar RCounterClient.java
rmic RCounter
% make run-RCounterClient
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar IRCounter.java
javac -classpath .:/略/GLUE-STD.jar:./略/servlet.jar RCounterClient.java
rmic RCounter
% make run-RCounterClient ![[←]](../icons/screen-return.gif) java RCounterClient localhost 8080
c1.value==11
c1.value==12
c1.value==13
% make run-RCounterClient
java RCounterClient localhost 8080
c1.value==11
c1.value==12
c1.value==13
% make run-RCounterClient ![[←]](../icons/screen-return.gif) java RCounterClient localhost 8080
c1.value==14
c1.value==15
c1.value==16
%
java RCounterClient localhost 8080
c1.value==14
c1.value==15
c1.value==16
% ![[]](../icons/screen-cursor.gif) 
RMI でオブジェクトを渡す時には、interface Serializable を implements する。
serialize 不要のフィールドには、transient をつける。
serialization は、RMI だけでなく、オブジェクトをファイルに落とす時にも 使える。
1: # 2: # counter.rb 3: # 4: 5: class Counter 6: def initialize(initVal) 7: @val = initVal 8: end 9: 10: def up() 11: @val += 1 12: end 13: 14: def getValue() 15: return @val 16: end 17: 18: def reset(newVal) 19: @val = newVal 20: end 21: end3つの手続きとコンストラクタからなる。
   1: #
   2: #       counter-local.rb
   3: #
   4: 
   5: require 'counter.rb'
   6: 
   7: def main(argv)
   8:         c1 = Counter.new( 10 )
   9:         0.upto(3-1) {|i|
  10:             c1.up()
  11:             printf("c1.value==%d\n",c1.getValue())
  12:         }
  13: end
  14: 
  15: main(ARGV)
Java に合わせるために、main() を作成している。
実行例:
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.rb ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-local.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-local.rb ![[←]](../icons/screen-return.gif) % make run-counter-local.rb
% make run-counter-local.rb ![[←]](../icons/screen-return.gif) ruby counter-local.rb
c1.value==11
c1.value==12
c1.value==13
% make run-counter-local.rb
ruby counter-local.rb
c1.value==11
c1.value==12
c1.value==13
% make run-counter-local.rb ![[←]](../icons/screen-return.gif) ruby counter-local.rb
c1.value==11
c1.value==12
c1.value==13
%
ruby counter-local.rb
c1.value==11
c1.value==12
c1.value==13
% ![[]](../icons/screen-cursor.gif) 
オブジェクト c1 を、main で作って実行している。実行する度に新しいオブ ジェクトが new される。
   1: #
   2: #       counter-server.rb
   3: #
   4: 
   5: require 'counter.rb'
   6: require 'drb/drb'
   7: 
   8: def main(argv)
   9:         if( argv.length != 1 )
  10:             $stderr.printf("Usage: %% ruby $0 port\n")
  11:             exit( 1 )
  12:         end
  13:         portno = argv[0]
  14:         url = "druby://localhost:" + portno
  15:         c1 = Counter.new( 10 )
  16:         DRb.start_service( url, c1 )
  17:         printf("startup server on %s\n", url)
  18:         printf("Ok.\n")
  19:         sleep()
  20: end
  21: 
  22: main(ARGV)
このプログラムは、1つの引数を取る。
This program takes one command line argument.
DRb.start_service() で、dRuby のサーバを起動している。その結、果新しい スレッドが作られる。このプログラムは、main() が終了してしまうと全体が終 了してしまうので、sleep() を呼んでmain() が終了しないようにしている。
   1: #
   2: #       counter-client.rb
   3: #
   4: 
   5: require 'drb/drb'
   6: 
   7: def main(argv)
   8:         if( argv.length != 2 )
   9:             $stderr.printf("Usage: %% ruby $0 hostname port\n")
  10:             exit( 1 )
  11:         end
  12:         hostname = argv[0]
  13:         portno   = argv[1]
  14:         url = "druby://" + hostname + ":" + portno
  15:         printf("url is %s\n",url)
  16:         c1 = DRbObject.new_with_uri(url)
  17:         0.upto(3-1) {|i|
  18:             c1.up()
  19:             printf("c1.value==%d\n",c1.getValue())
  20:         }
  21: end
  22: 
  23: main(ARGV)
このプログラムは、2つの引数を取る。
This program takes two command line arguments.
サーバは、は自動的に終了しないので、実験が終わったら ^C (Control-C)で 殺す。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.rb ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server.rb ![[←]](../icons/screen-return.gif) % make run-counter-server.rb
% make run-counter-server.rb ![[←]](../icons/screen-return.gif) ruby  -I. counter-server.rb 8080
startup server on druby://localhost:8080
Ok.
(最後に ^C で止める)
ruby  -I. counter-server.rb 8080
startup server on druby://localhost:8080
Ok.
(最後に ^C で止める)
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client.rb ![[←]](../icons/screen-return.gif) % make run-counter-client.rb
% make run-counter-client.rb ![[←]](../icons/screen-return.gif) ruby  -I. counter-client.rb localhost 8080
url is druby://localhost:8080
c1.value==11
c1.value==12
c1.value==13
% make run-counter-client.rb
ruby  -I. counter-client.rb localhost 8080
url is druby://localhost:8080
c1.value==11
c1.value==12
c1.value==13
% make run-counter-client.rb ![[←]](../icons/screen-return.gif) ruby  -I. counter-client.rb localhost 8080
url is druby://localhost:8080
c1.value==14
c1.value==15
c1.value==16
%
ruby  -I. counter-client.rb localhost 8080
url is druby://localhost:8080
c1.value==14
c1.value==15
c1.value==16
% ![[]](../icons/screen-cursor.gif) 
クライアントのプログラムを2回実行しても、同じカウンタが使われている所
に注意する。
   1: #
   2: #       counter-server-msgpack.rb
   3: #
   4: 
   5: require 'counter.rb'
   6: require 'msgpack/rpc'  #  gem install msgpack-rpc
   7: 
   8: def main(argv)
   9:         if( argv.length != 1 )
  10:             $stderr.printf("Usage: %% ruby $0 port\n")
  11:             exit( 1 )
  12:         end
  13:         portno = argv[0]
  14:         svr = MessagePack::RPC::Server.new()
  15:         c1 = Counter.new( 10 )
  16: #       svr.listen("0.0.0.0", portno, c1 )
  17:         svr.listen("::", portno, c1 )
  18:         printf("startup server at port number %s\n", portno )
  19:         svr.run()
  20: end
  21: 
  22: main(ARGV)
このプログラムは、1つの引数を取る。
This program takes one command line argument.
MessagePack::RPC::Server.new(), .run() で、 msgpack のサーバを起動している。その結、果新しい スレッドが作られる。
   1: #
   2: #       counter-client-msgpack.rb
   3: #
   4: 
   5: require 'msgpack/rpc'
   6: 
   7: def main(argv)
   8:         if( argv.length != 2 )
   9:             $stderr.printf("Usage: %% ruby $0 hostname port\n")
  10:             exit( 1 )
  11:         end
  12:         hostname = argv[0]
  13:         portno   = argv[1]
  14:         cli = MessagePack::RPC::Client.new(hostname, portno)
  15:         0.upto(3-1) {|i|
  16:             cli.call(:up)
  17:             printf("c1.value==%d\n",cli.call(:getValue))
  18:         }
  19: end
  20: 
  21: main(ARGV)
このプログラムは、2つの引数を取る。
This program takes two command line arguments.
このプログラムは、3回ループを回って、up() という手続きと getValue() と いう手続きを呼んでいる。合計、6 回のRPC が行われる。
実行する前に、次の gem をインストールする。
# gem install msgpack-rpc ![[←]](../icons/screen-return.gif) 
実行する時には、サーバ側とクライアント側でそれぞれ1つウインドウを開く。
サーバは、は自動的に終了しないので、実験が終わったら ^C (Control-C)で 殺す。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.rb ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server-msgpack.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server-msgpack.rb ![[←]](../icons/screen-return.gif) % make run-counter-server-msgpack.rb
% make run-counter-server-msgpack.rb ![[←]](../icons/screen-return.gif) ruby  -I. counter-server-msgpack.rb 8080
startup server at port number 8080
Ok.
(最後に ^C で止める)
ruby  -I. counter-server-msgpack.rb 8080
startup server at port number 8080
Ok.
(最後に ^C で止める)
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client-msgpack.rb
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client-msgpack.rb ![[←]](../icons/screen-return.gif) % make run-counter-client-msgpack.rb
% make run-counter-client-msgpack.rb ![[←]](../icons/screen-return.gif) ruby  -I. counter-client-msgpack.rb 127.0.0.1 8080
c1.value==11
c1.value==12
c1.value==13
% make run-counter-client-msgpack.rb
ruby  -I. counter-client-msgpack.rb 127.0.0.1 8080
c1.value==11
c1.value==12
c1.value==13
% make run-counter-client-msgpack.rb ![[←]](../icons/screen-return.gif) ruby  -I. counter-client-msgpack.rb 127.0.0.1 8080
c1.value==14
c1.value==15
c1.value==16
$
ruby  -I. counter-client-msgpack.rb 127.0.0.1 8080
c1.value==14
c1.value==15
c1.value==16
$ ![[]](../icons/screen-cursor.gif) 
クライアントのプログラムを2回実行しても、同じカウンタが使われている所
に注意する。
(localhost ではうまく動かないことがある。この例では、127.0.0.1 を使っている。)
1: # 2: # counter.py 3: # 4: 5: class Counter(object): 6: def __init__(self, initVal): 7: self.val = initVal 8: 9: def up(self): 10: self.val += 1 11: 12: def getValue(self): 13: return self.val 14: 15: def reset(self, newVal): 16: self.val = newVal 17:3つの手続きとコンストラクタからなる。
   1: #
   2: #       counter.py
   3: #
   4: 
   5: import sys
   6: import counter
   7: 
   8: def main(argv):
   9:     c1 = counter.Counter( 10 )
  10:     for i in range(3):
  11:         c1.up()
  12:         print(f'c1.value == {c1.getValue()}')
  13: 
  14: main(sys.argv)
Java, Ruby に合わせるために、main() を作成している。
python3 コマンドが見付からないと言われたら Makefile にある次の行をコメントアウトする。
#PYTHON = python
実行例:
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.py
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.py ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-local.py
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-local.py ![[←]](../icons/screen-return.gif) % make run-counter-local.py
% make run-counter-local.py ![[←]](../icons/screen-return.gif) python3 counter-local.py
c1.value==11
c1.value==12
c1.value==13
% make run-counter-local.py
python3 counter-local.py
c1.value==11
c1.value==12
c1.value==13
% make run-counter-local.py ![[←]](../icons/screen-return.gif) python3 counter-local.py
c1.value==11
c1.value==12
c1.value==13
%
python3 counter-local.py
c1.value==11
c1.value==12
c1.value==13
% ![[]](../icons/screen-cursor.gif) 
オブジェクト c1 を、main で作って実行している。実行する度に新しいオブ ジェクトが new される。
   1: #
   2: #       counter-server.py
   3: #
   4: 
   5: import xmlrpc.server
   6: import sys
   7: import counter  # ./counter.py
   8: 
   9: def main(argv):
  10:     if len(argv) != 2:
  11:         print(f'Usage: % python {argv[0]} port', file=sys.stderr)
  12:         sys.exit(1)
  13:     portno = argv[1]
  14:     hostname = 'localhost'
  15:     c1 = counter.Counter(10)
  16:     server = xmlrpc.server.SimpleXMLRPCServer((hostname, int(portno)), 
  17:                                               allow_none=True)
  18:     server.register_instance(c1)
  19:     url = f'http://{hostname}:{portno}'
  20:     print(f'startup server on {url}')
  21:     server.serve_forever()
  22: 
  23: main(sys.argv)
このプログラムは、1つの引数を取る。
This program takes one command line argument.
xmlrpc.server.SimpleXMLRPCServer() で、XML RPC のサーバを起動している。その結、果新しい スレッドが作られる。 register_instance() で、カウンタ・オブジェクトを登録している。 このプログラムは、main() が終了してしまうと全体が終 了してしまうので、server.serve_forever() を呼んでmain() が終了しないようにしている。
   1: #
   2: #       counter-client.py
   3: #
   4: 
   5: import xmlrpc.client
   6: import sys
   7: 
   8: def main(argv):
   9:     if len(argv) != 3:
  10:         print(f'Usage: % python {argv[0]} hostname port', file=sys.stderr)
  11:         sys.exit(1)
  12:     hostname = argv[1]
  13:     portno   = argv[2]
  14:     url = f'http://{hostname}:{portno}'
  15:     c1 = xmlrpc.client.ServerProxy(url)
  16:     for i in range(3):
  17:         c1.up()
  18:         print(f'c1.value == {c1.getValue()}')
  19: 
  20: main(sys.argv)
このプログラムは、2つの引数を取る。
This program takes two command line arguments.
サーバは、は自動的に終了しないので、実験が終わったら ^C (Control-C)で 殺す。
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.py
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter.py ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server.py
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-server.py ![[←]](../icons/screen-return.gif) $ make run-counter-server.py
$ make run-counter-server.py ![[←]](../icons/screen-return.gif) python3 counter-server.py 8080
startup server on http://localhost:8080
(最後に ^C で止める)
python3 counter-server.py 8080
startup server on http://localhost:8080
(最後に ^C で止める)
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/Makefile ![[←]](../icons/screen-return.gif) % wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client.py
% wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2023/2023-05-19/ex/counter-client.py ![[←]](../icons/screen-return.gif) $ make run-counter-client.py
$ make run-counter-client.py ![[←]](../icons/screen-return.gif) python3 counter-client.py localhost 8080
c1.value == 11
c1.value == 12
c1.value == 13
$ make run-counter-client.py
python3 counter-client.py localhost 8080
c1.value == 11
c1.value == 12
c1.value == 13
$ make run-counter-client.py ![[←]](../icons/screen-return.gif) python3 counter-client.py localhost 8080
c1.value == 14
c1.value == 15
c1.value == 16
$
python3 counter-client.py localhost 8080
c1.value == 14
c1.value == 15
c1.value == 16
$ ![[]](../icons/screen-cursor.gif) 
クライアントのプログラムを2回実行しても、同じカウンタが使われている所
に注意する。
JSON-RPC は、JSON を使ってデータをやりとりするRPC のライブラリ。
注意: 自動生成されたプログラム、たとえば、SunRPC のmain 関数を提出しな いように。
Notice: You should not submit automatically generated programs, such as the main function of a SunRPC server.
You can deal with only integer elements. You must write a single server program and two client programs. One client program adds an element to the container, and the other client program removes the element from the container.
ヒント: サーバにカウンタを生成する手続きを追加し、遠隔からアクセス可能にする。 個々の手続きには、カウンタのIDを含める。 Hints: Add a procedure that creates a counter, and other procures take the ID of a counter.
SunRPC, dRuby の場合は、名前が付けられないので面倒かもしれない。 Since SunRPC and dRuby do not allow naming objects, the program can be complex.
You can use the C, Java, Ruby, Python, Go, Rust, or Kotlin language. You should use an RPC mechanism in this Web page.
レポートには、次のものを含めなさい。 Your report must include following.
2023/05/22。 Glue (XML Web Services) のプログラムは、 次のホストで動きます。 The programs of Glue (XML Web Services) can run in the following hosts.
https://www.u.tsukuba.ac.jp/remote/#ssh
In the the program you wrote for Exercise (504) RPC programming, choose one of failures in RPC and cause the failure. Report the behavior of the programs.