例 4-12 には、clnt_tli_create() を使用して UDP トランスポートに対するクライアントを作成するルーチン clntudp_create() を示します。このプログラムでは、指定したトランスポートファミリに基づいたネットワーク選択方法を示します。clnt_tli_create() には、クライアントハンドルの作成のほかに次の 3 つの機能があります。
#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() の呼び出しにより閉じられます)、リトライのタイムアウト値を設定します。