The Lowest Layer of RPC from the Client Side
This section describes the lowest layer of RPC from the client side.
#include <stdio.h>
#include <rpc/rpc.h>
#include <utmp.h>
#include <rpcsvc/rusers.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
main(argc, argv)
int argc;
char **argv;
{
struct hostent *hp;
struct timeval pertry_timeout, total_timeout;
struct sockaddr_in server_addr;
int sock = RPC_ANYSOCK;
register CLIENT *client;
enum clnt_stat clnt_stat;
unsigned long nusers;
if (argc != 2) {
fprintf(stderr, "usage: nusers hostname\n");
exit(-1);
}
if ((hp = gethostbyname(argv[1])) == NULL) {
fprintf(stderr, "can't get addr for %s\n",argv[1]);
exit(-1);
}
pertry_timeout.tv_sec = 3;
pertry_timeout.tv_usec = 0;
bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
hp->h_length);
server_addr.sin_family = AF_INET;
server_addr.sin_port = 0;
if ((client = clntudp_create(&server_addr, RUSERSPROG,
RUSERSVERS, pertry_timeout, &sock)) == NULL) {
clnt_pcreateerror("clntudp_create");
exit(-1);
}
total_timeout.tv_sec = 20;
total_timeout.tv_usec = 0;
clnt_stat = clnt_call(client, RUSERSPROC_NUM, xdr_void,
0, xdr_u_long, &nusers, total_timeout);
if (clnt_stat != RPC_SUCCESS) {
clnt_perror(client, "rpc");
exit(-1);
}
clnt_destroy(client);
close(sock);
exit(0);
}
The low-level version of the callrpc routine is the clnt_call macro, which takes a CLIENT pointer rather than a host name. The parameters to the clnt_call macro are a CLIENT pointer, the procedure number, the XDR routine for serializing the argument, a pointer to the argument, the XDR routine for deserializing the return value, a pointer to where the return value is to be placed, and the total time in seconds to wait for a reply. Thus, the number of tries is the time out divided by the clntudp_create time out.
The CLIENT pointer is encoded with the transport mechanism. The callrpc routine uses UDP, thus it calls the clntudp_create routine to get a CLIENT pointer. To get Transmission Control Protocol (TCP), the programmer can call the clnttcp_create routine.
The parameters to the clntudp_create routine are the server address, the program number, the version number, a time-out value (between tries), and a pointer to a socket.
The clnt_destroy call always deallocates the space associated with the client handle. If the RPC library opened the socket associated with the client handle, the clnt_destroy macro closes it. If the socket was opened by the programmer, it stays open. In cases where there are multiple client handles using the same socket, it is possible to destroy one handle without closing the socket that other handles are using.
The stream connection is made when the call to the clntudp_create macro is replaced by a call to the clnttcp_create routine.
clnttcp_create(&server_addr, prognum, versnum, &sock,
inputsize, outputsize);
transp = svctcp_create(RPC_ANYSOCK, 0, 0);
The last two arguments to the svctcp_create routine are send and receive sizes, respectively. If 0 is specified for either of these, the system chooses a reasonable default.