トランスポート独立の RPC ルーチン (TI-RPC ルーチン) を使用すると、アプリケーション開発者はトランスポート層へのアクセスレベルを自由に選択できます。最上位レベルのルーチンは、トランスポートが完全に抽象化されて、本当の意味でトランスポート独立になっています。下位レベルのルーチンを使用すると、旧バージョンと同じように個々のトランスポートに依存したアクセスレベルになります。
この節は、トランスポート特定 RPC (TS-RPC) アプリケーションを TI-RPC へ移行するための非公式ガイドになっています。表 4-11 では、いくつかのルーチンを選んで相違点を示します。ソケットとトランスポート層インタフェース (TLI) の移行の問題点についての詳細は、『Transport Interfaces Programming Guide』を参照してください。
TCP または UDP に基づくアプリケーションはバイナリ互換モードで実行できます。すべてのソースファイルをコンパイルし直したり、リンクし直したりできるのは、一部のアプリケーションだけです。RPC 呼び出しだけを使用し、ソケット、TCP、UDP に固有の機能を使用していないアプリケーションがこれに当たります。
ソケットセマンティクスに依存していたり、TCP や UDP の固有の機能を使用しているアプリケーションでは、コードの変更や追加が必要な場合があります。ホストアドレス形式を使用したり、バークレイ UNIX の特権ポートを使用するアプリケーションがこれに当たります。
ライブラリ内部や個々のソケット仕様に依存していたり、特定のトランスポートアドレスに依存するアプリケーションは、移行の手間も大きく、本質的な変更が必要な場合もあります。
アプリケーションがトランスポート独立になるため、より多くのトランスポート上で実行できます。
アプリケーションの効率を改善する新規インタフェースが使用できます。
バイナリレベルの互換性の影響は、ネイティブモードより少なくなります。
旧インタフェースは、将来のバージョンで使用できなくなる可能性があります。
IPv6 は、IPv4 の後継バージョンで、今日のインターネットテクノロジーにおいて、最も一般的に使用される階層 2 のプロトコルです。また、IPv6 は IP の次世代 (IPng) とも呼ばれています。詳細については、『Solaris のシステム管理 (第 3 巻)』を参照してください。
ユーザーは、IPv4 と IPv6 の両方を使用できます。COTS (コネクション型のトランスポートサービス) を使用する場合、アプリケーションにより、使用する「スタック」が選択されます。この場合、TCP または CLTS (コネクションレス型のトランスポートサービス) を選択できます。
次の図では、IPv4 または IPv6 プロトコルスタックを経由して実行される、典型的な RPC アプリケーションを示しています。
IPv6 がサポートされるのは、TI-RPC アプリケーションだけです。現在、TS-RPC では、IPv6 をサポートしていません。TI-RPC におけるトランスポートの選択は、NETPATH 環境変数、または /etc/netconfig のいずれかによって制御されます。IPv4 または IPv6 の代わりに、TCP または UDP を選択するかどうかは、/etc/netconfig 内の該当エントリの順序によって決まります。/etc/netconfig には、IPv6 に関連する新しいエントリが 2 つあります。また、デフォルトでは、これらのエントリは、ファイルの最初の 2 つのエントリです。まず、TI-RPC が IPv6 を試行し、失敗すると、IPv4 を試行します。この場合、RPC アプリケーション自体に変更が無いこと (つまり、トランスポートに関する情報がまったく無く、トップレベルのインタフェースを使用して書かれたものであること) が必要条件です。
clnt_create() |
svc_create() |
clnt_call() |
clnt_create_timed() |
このインタフェースでは、/etc/netconfig 内の最初のエントリが IPv6 である場合、IPv6 が自動的に選択されます。
IPv6 を使用すると、アプリケーションは、RPCBIND プロトコルの V3 と V4 だけを使用して、サービスバーと番号を配置できます。
clnt_tli_create() |
svc_tli_create() |
clnt_dg_create() |
svc_dg_create() |
clnt_vc_create() |
svc_vc_create() |
上記のいずれかのインタフェースを使用する場合は、コードを移植する必要があります。
ネットワーク関数は libc から外されました。コンパイル時には libnsl を明示的に指定して、ネットワーク・サービス・ルーチンをリンクする必要があります。
旧インタフェースの多くは libnsl ライブラリでもサポートされていますが、TCP と UDP でしか使用できません。それ以外の新たなトランスポートを利用するには、新インタフェースを使用する必要があります。
トランスポート独立にするには、アドレスを直接使用できません。すなわち、アプリケーションでアドレス変換を行う必要があります。
トランスポート独立型の RPC とトランスポート特定の RPC との主な相違点を表 4-11 に示します。TI-RPC と TS-RPC の比較については、「旧バージョンとの比較」 のサンプルプログラムを参照してください。
表 4-11 TI-RPC と TS-RPC の相違点
この節では、RPC ライブラリ関数を機能別にグループ化して示します。各グループ内では、旧バージョンと同じ関数、機能が追加された関数、旧バージョンにはなかった新規関数に分けて示します。
アスタリスクの付いた関数は、新バージョンへの移行期間はサポートされていますが、Solaris の将来のバージョンではなくなる可能性があります。
次の関数は、旧バージョンと同じで、現在の SunOS で使用できます。
clnt_destroy clnt_pcreateerror *clntraw_create clnt_spcreateerror *clnttcp_create *clntudp_bufcreate *clntudp_create clnt_control clnt_create clnt_create_timed clnt_create_vers clnt_dg_create clnt_raw_create clnt_tli_create clnt_tp_create clnt_tp_create_timed clnt_vc_create
次の関数は、旧バージョンと同じで、現在の SunOS で使用できます。
svc_destroy svcfd_create *svc_raw_create *svc_tp_create *svcudp_create *svc_udp_bufcreate svc_create svc_dg_create svc_fd_create svc_raw_create svc_tli_create svc_tp_create svc_vc_create
次の関数は、旧バージョンと同じで、現在の SunOS で使用できます。
*registerrpc *svc_register *svc_unregister xprt_register xprt_unregister rpc_reg svc_reg svc_unreg
次の関数は、旧バージョンと同じで、現在の SunOS で使用できます。
*callrpc clnt_call *svc_getcaller - IP に基づくトランスポートでのみ使用可 rpc_call svc_getrpccaller
次の関数の機能は旧バージョンと同じです。旧バージョンとの互換性を保つためにだけサポートされています。
*clnt_broadcast
clnt_broadcast() は portmap サービスにだけブロードキャストできます。
portmap と rpcbind の両方にブロードキャストできる次の関数が現在の SunOS で使用できます。
rpc_broadcast
TI-RPC ライブラリ関数は、portmap と rpcbind の両方で使用できますが、それぞれサービスが異なるため、次のように 2 組の関数が提供されています。
次の関数は portmap と共に使用します。
pmap_set pmap_unset pmap_getport pmap_getmaps pmap_rmtcall
次の関数は rpcbind と共に使用します。
rpcb_set rpcb_unset rpcb_getaddr rpcb_getmaps rpcb_rmtcall
次の関数の機能は旧バージョンと同じです。旧バージョンとの互換性を保つためにだけサポートされています。
authdes_create authunix_create authunix_create_default authdes_seccreate authsys_create authsys_create_default
現バージョンの rpcbind ではタイムサービス (主として、安全な RPC のためにクライアント側とサーバー側の時間を同期させるときに使用) が提供されており、rpcb_gettime() 関数で利用できます。pmap_getport() と rpcb_getaddr() は、登録サービスのポート番号を取り出すときに使用します。サーバーでバージョンが 2、3、4 の rcpbind が実行されている場合には、rpcb_getaddr() を使用します。pmap_getport() はバージョン 2 が実行されている場合しか使用できません。
例 4-47 と 例 4-48では、クライアント作成部分が TS-RPC と TI-RPC とでどう違うかを示します。どちらのプログラムも次のことを実行します。
UDP 記述子を作成します。
遠隔ホストの RPC 結合プロセスと通信してサービスアドレスを得ます。
遠隔サービスのアドレスを記述子に結合します。
クライアントハンドルを作成してタイムアウト値を設定します。
struct hostent *h; struct sockaddr_in sin; int sock = RPC_ANYSOCK; u_short port; struct timeval wait; if ((h = gethostbyname( "host" )) == (struct hostent *) NULL) { syslog(LOG_ERR, "gethostbyname failed"); exit(1); } sin.sin_addr.s_addr = *(u_int *) hp->h_addr; if ((port = pmap_getport(&sin, PROGRAM, VERSION, "udp")) == 0) { syslog (LOG_ERR, "pmap_getport failed"); exit(1); } else sin.sin_port = htons(port); wait.tv_sec = 25; wait.tv_usec = 0; clntudp_create(&sin, PROGRAM, VERSION, wait, &sock); |
TI-RPC では、UDP トランスポートは netid udp を持つものとみなします。netid はよく知られた名前でなくてもかまいません。
struct netconfig *nconf; struct netconfig *getnetconfigent(); struct t_bind *tbind; struct timeval wait; nconf = getnetconfigent("udp"); if (nconf == (struct netconfig *) NULL) { syslog(LOG_ERR, "getnetconfigent for udp failed"); exit(1); } fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL); if (fd == -1) { syslog(LOG_ERR, "t_open failed"); exit(1); } tbind = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); if (tbind == (struct t_bind *) NULL) { syslog(LOG_ERR, "t_bind failed"); exit(1); } if (rpcb_getaddr( PROGRAM, VERSION, nconf, &tbind->addr, "host") == FALSE) { syslog(LOG_ERR, "rpcb_getaddr failed"); exit(1); } cl = clnt_tli_create(fd, nconf, &tbind->addr, PROGRAM, VERSION, 0, 0); (void) t_free((char *) tbind, T_BIND); if (cl == (CLIENT *) NULL) { syslog(LOG_ERR, "clnt_tli_create failed"); exit(1); } wait.tv_sec = 25; wait.tv_usec = 0; clnt_control(cl, CLSET_TIMEOUT, (char *) &wait); |
例 4-49 と例 4-50では、ブロードキャスト部分が旧バージョンと SunOS 5.x とでどう違うかを示します。SunOS 4.x の clnt_broadcast() は SunOS 5.x の rpc_broadcast() とほぼ同じです。大きく異なるのは、collectnames() 関数で重複アドレスを削除し、ブロードキャストに応答したホスト名を表示する点です。
statstime sw; extern int collectnames(); clnt_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS, xdr_void, NULL, xdr_statstime, &sw, collectnames); ... collectnames(resultsp, raddrp) char *resultsp; struct sockaddr_in *raddrp; { u_int addr; struct entry *entryp, *lim; struct hostent *hp; extern int curentry; /* 重複アドレスはカット */ addr = raddrp->sin_addr.s_addr; lim = entry + curentry; for (entryp = entry; entryp < lim; entryp++) if (addr == entryp->addr) return (0); ... /* ホスト名がわかればホスト名、わからなければアドレスを表示 */ hp = gethostbyaddr(&raddrp->sin_addr.s_addr, sizeof(u_int), AF_INET); if( hp == (struct hostent *) NULL) printf("0x%x", addr); else printf("%s", hp->h_name); } |
例 4-50 は、TI-RPC におけるブロードキャストを示します。
statstime sw; extern int collectnames(); rpc_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS, xdr_void, NULL, xdr_statstime, &sw, collectnames, (char *) 0); ... collectnames(resultsp, taddr, nconf) char *resultsp; struct t_bind *taddr; struct netconfig *nconf; { struct entry *entryp, *lim; struct nd_hostservlist *hs; extern int curentry; extern int netbufeq(); /* 重複アドレスはカット */ lim = entry + curentry; for (entryp = entry; entryp < lim; entryp++) if (netbufeq( &taddr->addr, entryp->addr)) return (0); ... /* ホスト名がわかればホスト名、わからなければアドレスを表示 */ if (netdir_getbyaddr( nconf, &hs, &taddr->addr ) == ND_OK) printf("%s", hs->h_hostservs->h_host); else { char *uaddr = taddr2uaddr(nconf, &taddr->addr); if (uaddr) { printf("%s¥n", uaddr); (void) free(uaddr); } else printf("unknown"); } } netbufeq(a, b) struct netbuf *a, *b; { return(a->len == b->len && !memcmp( a->buf, b->buf, a->len)); } |