In this example, the netconfig structure is
obtained by a call to getnetconfigent(netid). See the getnetconfig(3NSL)man
page and Programming
Interfaces Guide for more details. At this level, the
program explicitly selects the network.
To bound the time allowed for client handle creation in the previous
example to 30 seconds, replace the call to clnt_tp_create()
with a call to clnt_tp_create_timed() as shown in the following
code segment:
struct timeval timeout;
timeout.tv_sec = 30; /* 30 seconds */
timeout.tv_usec = 0;
client = clnt_tp_create_timed(argv[1],
TIME_PROG, TIME_VERS, nconf,
&timeout);
|
Server Side of the Intermediate-Level Interface
The following example shows the corresponding server. The command line
that starts the service must specify the transport over which the service
is provided.
Example 4-11 Server for Time Service, Intermediate Level
/*
* This program supplies Greenwich mean
* time to the client that invokes it.
* The call format is: server netid
*/
#include <stdio.h>
#include <rpc/rpc.h>
#include <netconfig.h> /* For netconfig structure */
#include "time_prot.h"
static void time_prog();
main(argc, argv)
int argc;
char *argv[];
{
SVCXPRT *transp;
struct netconfig *nconf;
if (argc != 2) {
fprintf(stderr, "usage: %s netid\n",
argv[0]);
exit(1);
}
if ((nconf = getnetconfigent( argv[1])) ==
(struct netconfig *) NULL) {
fprintf(stderr, "Could not find info on %s\n",
argv[1]);
exit(1);
}
transp = svc_tp_create(time_prog, TIME_PROG,
TIME_VERS, nconf);
if (transp == (SVCXPRT *) NULL) {
fprintf(stderr, "%s: cannot create
%s service\n", argv[0], argv[1]);
exit(1)
}
freenetconfigent(nconf);
svc_run();
}
static
void time_prog(rqstp, transp)
struct svc_req *rqstp;
SVCXPRT *transp;
{
/* Code identical to Top Level version */
|
Expert-Level Interface
At the expert level, network selection is done the same as at the intermediate
level. The only difference is in the increased level of control that the application
has over the details of the CLIENT and SVCXPRT handles. These examples illustrate this control, which is exercised
using the clnt_tli_create() and svc_tli_create() routines. For more information on TLI, see Programming Interfaces Guide.
Client Side of the Expert-Level Interface
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:
Pass an open TLI file descriptor, which might or might not
be bound
Pass the server's address to the client
Specify the send and receive buffer size
Example 4-12 Client for RPC Lower Level
#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);
}
|