ONC+ 開発ガイド

エキスパートレベルのインタフェース

エキスパートレベルのネットワーク選択は、中間レベルと同じです。中間レベルとの唯一の違いは、アプリケーションから CLIENTSVCXPRT のハンドルをより詳細に制御できる点です。次の例では、clnt_tli_create()svc_tli_create() の 2 つのルーチンを使用した制御方法を示します。TLI についての詳細は、『プログラミングインタフェース』 を参照してください。

クライアント側

例 4–12 には、clnt_tli_create() を使用して UDP トランスポートに対するクライアントを作成するルーチン clntudp_create() を示します。このプログラムでは、指定したトランスポートファミリに基づいたネットワークの選択方法を示します。clnt_tli_create() には、クライアントハンドルの作成のほかに次の 3 つの機能があります。


例 4–12 下位レベル RPC 使用に対するクライアント側プログラム

#include <stdio.h>
#include <rpc/rpc.h>
#include <netconfig.h>
#include <netinet/in.h>
/*
 * 旧バージョンの RPC では、TCP/IP と UDP/IP だけがサポートされていました。 
 * 現バージョンの clntudp_create() は TLI/STREAMS に基づいています。
 */
CLIENT *
clntudp_create(raddr, prog, vers, wait, sockp)
	struct sockaddr_in *raddr;		/* リモートアドレス */
	rpcprog_t prog;	 				/* プログラム番号 */
	prcvers_t vers;					/* バージョン番号 */
	struct timeval wait;				/* 待ち時間 */
	int *sockp;                         /* ファイル記述子 (fd) のポインタ */
{
	CLIENT *cl;			/* クライアントハンドル */
 int madefd = FALSE;			/* fd はオープンされているか */
	int fd = *sockp;			/* TLI の fd */
	struct t_bind *tbind;			/* 結合アドレス */
 struct netconfig *nconf;			/* netconfig 構造体 */
	void *handlep;
 
	if ((handlep = setnetconfig() ) == (void *) NULL) {
		/* ネットワーク設定開始でのエラー */
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
 	return((CLIENT *) NULL);
	}
	/*
  * 非接続型で、プロトコルファミリが INET、名前が UDP の 
	 * トランスポートが見つかるまで探す。
  */
	while (nconf = getnetconfig( handlep)) {
 	if ((nconf->nc_semantics == NC_TPI_CLTS) &&
		     (strcmp( nconf->nc_protofmly, NC_INET ) == 0) &&
 	     (strcmp( nconf->nc_proto, NC_UDP ) == 0))
		 break;
 }
	if (nconf == (struct netconfig *) NULL)
 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		goto err;
 }
	if (fd == RPC_ANYFD) {
		fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
		if (fd == -1) {
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
			goto err;
		}
 }
	if (raddr->sin_port == 0) { /* 未知のリモートアドレス */
 	u_short sport;
		/*
		 * ユーザー作成のルーチン rpcb_getport() は 
                 * rpcb_getaddr を呼び出して、
		 * netbuf アドレスをホストのバイト順序に従って
		 * ポート番号に変換する
 	 */
		sport = rpcb_getport(raddr, prog, vers, nconf);
 	if (sport == 0) {
			rpc_createerr.cf_stat = RPC_PROGUNAVAIL;
 		goto err;
		}
		raddr->sin_port = htons(sport);
 }
	/* sockaddr_in を netbuf に変換 */
	tbind = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
	if (tbind == (struct t_bind *) NULL)
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 	goto err;
	}
	if (t_bind->addr.maxlen < sizeof( struct sockaddr_in))
		goto err;
	(void) memcpy( tbind->addr.buf, (char *)raddr,
	               sizeof(struct sockaddr_in));
	tbind->addr.len = sizeof(struct sockaddr_in);
 /* fd を結合 */
	if (t_bind( fd, NULL, NULL) == -1) {
 	rpc_createerr.ct_stat = RPC_TLIERROR;
		goto err;
 }
	cl = clnt_tli_create(fd, nconf, &(tbind->addr), prog, vers,
	                      tinfo.tsdu, tinfo.tsdu);
 /* netconfig ファイルを閉じる */
	(void) endnetconfig( handlep);
 (void) t_free((char *) tbind, T_BIND);
	if (cl) {
 	*sockp = fd;
		if (madefd == TRUE) {
			/* fd はハンドルの破棄と同時に閉じる */
			(void)clnt_control(cl,CLSET_FD_CLOSE, (char *)NULL);
		}
		/* リトライ時間の設定 */
 	(void) clnt_control( l, CLSET_RETRY_TIMEOUT,
		                     (char *) &wait);
		return(cl);
 }
err:
	if (madefd == TRUE)
 	(void) t_close(fd);
	(void) endnetconfig(handlep);
 return((CLIENT *) NULL);
}

ネットワーク (トランスポート) 選択には、setnetconfig()getnetconfig()endnetconfig() を使用します。endnetconfig() の呼び出しは、プログラムの終り近くの clnt_tli_create() の呼び出しの後で行なっていることに注意してください。

clntudp_create() には、オープンしている TLI ファイル記述子を渡すことができます。ファイル記述子が渡されなかった場合 (fd == RPC_ANYFD)、 clntudp_create() は、t_open() に渡すデバイス名を UDP の netconfig 構造体から取り出して自分でオープンします。

リモートアドレスがわからない場合 (raddr->sin_port == 0) は、リモートの rpcbind デーモンを使って取り出します。

クライアントハンドルが作成されれば、clnt_control() を使用してさまざまな変更を加えることができます。RPC ライブラリは、ハンドルを破棄するときにファイル記述子を閉じます。fd 自体をオープンしたときには、clnt_destroy () を呼び出して閉じます。 そして、リトライのタイムアウト値を設定します。

サーバー側

例 4–13 に、対応するサーバー側プログラム svcudp_create() を示します。サーバー側では svc_tli_create() を使用します。

svc_tli_create() は、アプリケーションで次のように詳細な制御を行う必要があるときに使用します。

サービスを rpcbind により登録するには、rpcb_set() を使用します。


例 4–13 下位レベル RPC を使用したサーバー側プログラム

#include <stdio.h>
#include <rpc/rpc.h>
#include <netconfig.h>
#include <netinet/in.h>
 
SVCXPRT *
svcudp_create(fd)
	register int fd;
{
 struct netconfig *nconf;
	SVCXPRT *svc;
	int madefd = FALSE;
	int port;
	void *handlep;
 struct  t_info tinfo;
 
	/* どのトランスポートも使用不可の場合 */
 if ((handlep = setnetconfig() ) == (void *) NULL) {
		nc_perror("server");
 	return((SVCXPRT *) NULL);
	}
	/*
  * 非接続型で、プロトコルファミリが INET、名前が UDP の
	 * トランスポートが見つかるまで探す。
  */
	while (nconf = getnetconfig( handlep)) {
 	if ((nconf->nc_semantics == NC_TPI_CLTS) &&
		    (strcmp( nconf->nc_protofmly, NC_INET) == 0 )&&
		    (strcmp( nconf->nc_proto, NC_UDP) == 0 ))
			break;
 }
	if (nconf == (struct netconfig *) NULL) {
 	endnetconfig(handlep);
		return((SVCXPRT *) NULL);
 }
	if (fd == RPC_ANYFD) {
		fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
		if (fd == -1) {
			(void) endnetconfig();
			return((SVCXPRT *) NULL);
 	}
		madefd = TRUE;
	} else
 	t_getinfo(fd, &tinfo);
	svc = svc_tli_create(fd, nconf, (struct t_bind *) NULL,
	                      tinfo.tsdu, tinfo.tsdu);
	(void) endnetconfig(handlep);
 if (svc == (SVCXPRT *) NULL) {
		if (madefd)
 		(void) t_close(fd);
		return((SVCXPRT *)NULL);
 }
	return (svc);
}

この例では、clntudp_create() と同じ方法でネットワーク選択を行なっています。svc_tli_create() で結合しているため、ファイル記述子は明示的にはトランスポートアドレスと結合されません。

svcudp_create() はオープンしている fd を使用できます。有効なファイル記述子が渡されなければ、選択された netconfig 構造体を使用してこのルーチン内でオープンします。