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 */ u_long prog; /* Program number */ u_long 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.