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;		/* 遠隔アドレス */
	u_long prog;							/* プログラム番号 */
	u_long 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) { /* remote addr unknown */
		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() の呼び出しにより閉じられます)、リトライのタイムアウト値を設定します。