Example 4-12 shows a version of clntudp_create() (the client creation routine for UDP transport) using clnt_tli_create(). The example shows how to do network selection based on the family of the transport you choose. clnt_tli_create() is used to create a client handle and to:
#include <stdio.h>
#include <rpc/rpc.h>
#include <netconfig.h>
#include <netinet/in.h>
/*
* In earlier implementations of RPC,
* only TCP/IP and UDP/IP were supported.
* This version of clntudp_create()
* is based on TLI/Streams.
*/
CLIENT *
clntudp_create(raddr, prog, vers, wait, sockp)
struct sockaddr_in *raddr; /* Remote address */
rpcprog_t prog; /* Program number */
prcvers_t vers; /* Version number */
struct timeval wait; /* Time to wait */
int *sockp; /* fd pointer */
{
CLIENT *cl; /* Client handle */
int madefd = FALSE; /* Is fd opened here */
int fd = *sockp; /* TLI fd */
struct t_bind *tbind; /* bind address */
struct netconfig *nconf; /* netconfig structure */
void *handlep;
if ((handlep = setnetconfig() ) == (void *) NULL) {
/* Error starting network configuration */
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
return((CLIENT *) NULL);
}
/*
* Try all the transports until it gets one that is
* connectionless, family is INET, and preferred name is 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() is a user-provided routine that calls
* rpcb_getaddr and translates the netbuf address to port
* number in host byte order.
*/
sport = rpcb_getport(raddr, prog, vers, nconf);
if (sport == 0) {
rpc_createerr.cf_stat = RPC_PROGUNAVAIL;
goto err;
}
raddr->sin_port = htons(sport);
}
/* Transform sockaddr_in to 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);
/* Bind 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);
/* Close the netconfig file */
(void) endnetconfig( handlep);
(void) t_free((char *) tbind, T_BIND);
if (cl) {
*sockp = fd;
if (madefd == TRUE) {
/* fd should be closed while destroying the handle */
(void)clnt_control(cl,CLSET_FD_CLOSE, (char *)NULL);
}
/* Set the retry time */
(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);
}
|
The network is selected using setnetconfig(), getnetconfig(), and endnetconfig().
endnetconfig() is not called until after the call to clnt_tli_create(), near the end of the example.
clntudp_create() can be passed an open TLI fd. If passed none (fd == RPC_ANYFD), it opens its own using the netconfig structure for UDP to find the name of the device to pass to t_open().
If the remote address is not known (raddr->sin_port == 0), it is obtained from the remote rpcbind.
After the client handle has been created, you can modify it using calls to clnt_control(). The RPC library closes the file descriptor when destroying the handle (as it does with a call to clnt_destroy() when it opens the fd itself) and sets the retry time-out period.