ONC+ 開発ガイド

クライアント側

例 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) は、t_open() に渡すデバイス名を UDP の netconfig 構造体から取り出して自分でオープンします。

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

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