Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
  Previous   Contents   Next 
   
 
Chapter 4

Programmer's Interface to RPC

This chapter addresses the C interface to RPC and describes how to write network applications using RPC. For a complete specification of the routines in the RPC library, see the rpc(3NSL) and related man pages.

The topics covered in this chapter include:


Note - The client and server interfaces described in this chapter are multithread safe, except where noted (such as raw mode). This designation means that applications that contain RPC function calls can be used freely in a multithreaded application.


Simplified Interface

The simplified interface is the level to use if you do not require the use of any other RPC routines. This level also limits control of the underlying communications mechanisms. You can rapidly develop a program at this level, and the development is directly supported by the rpcgen compiler. For most applications, rpcgen and its facilities are sufficient.

Some RPC services are not available as C functions, but they are available as RPC programs. The simplified interface library routines provide direct access to the RPC facilities for programs that do not require fine levels of control. Routines such as rusers() are in the RPC services library librpcsvc. The following code example is a program that displays the number of users on a remote host. It calls the RPC library routine rusers().


Example 4-1 rusers Program

#include <rpc/rpc.h>

#include <rpcsvc/rusers.h>
#include <stdio.h>
 
/*
 * a program that calls the
 * rusers() service
 */
 
main(argc, argv)
	int argc;
	char **argv;
{
	int num;
 
	if (argc != 2) {
		fprintf(stderr, "usage: %s hostname\n",
						argv[0]);
		exit(1);
	}
	if ((num = rnusers(argv[1])) < 0) {
		fprintf(stderr, "error: rusers\n");
		exit(1);
	}
	fprintf(stderr, "%d users on %s\n", num,
						argv[1] );
	exit(0);}

Compile the program in Example 4-1 by typing:

cc program.c -lrpcsvc -lnsl

Client Side of Simplified Interface

Just one function exists on the client side of the simplified interface: rpc_call(). It has nine parameters:

int	0 or error code
rpc_call (  
       char				*host			/* Name of server host */
       rpcprog_t		prognum		/* Server program number */
       rpcvers_t		versnum		/* Server version number */
       rpcproc_t		procnum		/* Server procedure number */
       xdrproc_t		inproc		/* XDR filter to encode arg */
       char				*in			/* Pointer to argument */
       xdr_proc_t	outproc		/* Filter to decode result */
       char				*out			/* Address to store result */
       char		*nettype	 /*For transport selection */
);

The rpc_call() function calls the procedure specified by prognum, versum, and procnum on the host. The argument to be passed to the remote procedure is pointed to by the in parameter, and inproc is the XDR filter to encode this argument. The out parameter is an address where the result from the remote procedure is to be placed. outproc is an XDR filter that decodes the result and places it at this address.

The client blocks on rpc_call() until it receives a reply from the server. If the server accepts, it returns RPC_SUCCESS with the value of zero. The server returns a non-zero value if the call was unsuccessful. You can cast this value to the type clnt_stat, an enumerated type defined in the RPC include files and interpreted by the clnt_sperrno() function. This function returns a pointer to a standard RPC error message corresponding to the error code.

In the example, all "visible" transports listed in /etc/netconfig are tried. Adjusting the number of retries requires use of the lower levels of the RPC library.

Multiple arguments and results are handled by collecting them in structures.

The following code example changes the code in Example 4-1 to use the simplified interface.


Example 4-2 rusers Program Using Simplified Interface

#include <stdio.h>

#include <utmpx.h>
#include <rpc/rpc.h>

#include <rpcsvc/rusers.h>
 
/* a program that calls the RUSERSPROG
 * RPC program
 */
 
main(argc, argv)
	int argc;
	char **argv;
{
	unsigned int nusers;
	enum clnt_stat cs;
 
	if (argc != 2) {
		   fprintf(stderr, "usage: rusers hostname\n");
		   exit(1);
	}
	if( cs = rpc_call(argv[1], RUSERSPROG,
			 	RUSERSVERS,	RUSERSPROC_NUM, xdr_void,
					(char *)0, xdr_u_int, (char *)&nusers,
     "visible")  !=  RPC_SUCCESS )  {
		          clnt_perrno(cs);
		          exit(1);
	           }
	fprintf(stderr, "%d users on %s\n", nusers,
						argv[1] );
	exit(0);
}

Data types can be represented differently on different machines. Therefore, rpc_call() needs both the type of the RPC argument and a pointer to it. rpc_call() also needs this information for the result. For RUSERSPROC_NUM, the return value is an unsigned int, so the first return parameter of rpc_call() is xdr_u_int, which is for an unsigned int, and the second return parameter is &nusers, which points to unsigned int storage. Because RUSERSPROC_NUM has no argument, the XDR encoding function of rpc_call() is xdr_void() and its argument is NULL.

 
 
 
  Previous   Contents   Next