Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
5.  Advanced RPC Programming Techniques Broadcast RPC  Previous   Contents   Next 
   
 

The function in Example 5-3 collects the replies to the broadcast. The normal operation is to collect either the first reply or all replies. bcast_proc() displays the IP address of the server that has responded. Because the function returns FALSE it continues to collect responses. The RPC client code continues to resend the broadcast until it times out.


Example 5-3 Collect Broadcast Replies

bool_t
bcast_proc(res, t_addr, nconf)
	void *res;									/* Nothing comes back */
	struct t_bind *t_addr;					/* Who sent us the reply */
	struct netconfig *nconf;
{
	register struct hostent *hp;
	char *naddr;

	naddr = taddr2naddr(nconf, &taddr->addr);
	if (naddr == (char *) NULL) {
		fprintf(stderr,"Responded: unknown\n");
	} else {
		fprintf(stderr,"Responded: %s\n", naddr);
		free(naddr);
	}
	return(FALSE);
}

If done is TRUE, then broadcasting stops and rpc_broadcast() returns successfully. Otherwise, the routine waits for another response. The request is rebroadcast after a few seconds of waiting. If no responses come back, the routine returns with RPC_TIMEDOUT.

Batching

RPC is designed so that clients send a call message and wait for servers to reply to the call. This procedure implies that a client is blocked while the server processes the call. This result is inefficient when the client does not need each message acknowledged.

RPC batching lets clients process asynchronously. RPC messages can be placed in a pipeline of calls to a server. Batching requires that:

  • The server does not respond to any intermediate message.

  • The pipeline of calls is transported on a reliable transport, such as TCP.

  • The result's XDR routine in the calls is NULL.

  • The RPC call's timeout is zero.

Because the server does not respond to each call, the client can send new calls in parallel with the server processing previous calls. The transport can buffer many call messages and send them to the server in one write() system call. This buffering decreases interprocess communication overhead and the total time of a series of calls. The client should end with a nonbatched call to flush the pipeline.

The following code example shows the unbatched version of the client. It scans the character array, buf, for delimited strings and sends each string to the server.


Example 5-4 Unbatched Client

#include <stdio.h>

#include <rpc/rpc.h>
#include "windows.h"
 
main(argc, argv)
	int argc;
	char **argv;
{
	struct timeval total_timeout;
	register CLIENT *client;
	enum clnt_stat clnt_stat;
	char buf[1000], *s = buf;
 
	if ((client = clnt_create( argv[1], WINDOWPROG, WINDOWVERS,
								"circuit_v")) == (CLIENT *) NULL) {
		clnt_pcreateerror("clnt_create");
		exit(1);
	}
 
	total_timeout.tv_sec = 20;
	total_timeout.tv_usec = 0;
	while (scanf( "%s", s ) != EOF) {
		if (clnt_call(client, RENDERSTRING, xdr_wrapstring, &s,
		   xdr_void, (caddr_t) NULL, total_timeout) != RPC_SUCCESS) {
			clnt_perror(client, "rpc");
			exit(1);
		}
	}
 
	clnt_destroy( client );
	exit(0);
}

The following code example shows the batched version of the client. It does not wait after each string is sent to the server. It waits only for an ending response from the server.


Example 5-5 Batched Client

#include <stdio.h>

#include <rpc/rpc.h>
#include "windows.h"
 
main(argc, argv)
	int argc;
	char **argv;
{
	struct timeval total_timeout;
	register CLIENT *client;
	enum clnt_stat clnt_stat;
	char buf[1000], *s = buf;
 
	if ((client = clnt_create( argv[1], WINDOWPROG, WINDOWVERS,
									"circuit_v")) == (CLIENT *) NULL) {
		clnt_pcreateerror("clnt_create");
		exit(1);
	}
	timerclear(&total_timeout);
	while (scanf("%s", s) != EOF)
		clnt_call(client, RENDERSTRING_BATCHED, xdr_wrapstring,
		           &s, xdr_void, (caddr_t) NULL, total_timeout);
	/* Now flush the pipeline */
	total_timeout.tv_sec = 20;
	clnt_stat = clnt_call(client, NULLPROC, xdr_void,
	         (caddr_t) NULL, xdr_void, (caddr_t) NULL,
total_timeout);
	if (clnt_stat != RPC_SUCCESS) {
		clnt_perror(client, "rpc");
		exit(1);
	}
	clnt_destroy(client);
	exit(0);
}

The following code example shows the dispatch portion of the batched server. Because the server sends no message, the clients are not notified of failures.


Example 5-6 Batched Server

#include <stdio.h>

#include <rpc/rpc.h>
#include "windows.h"
 
void
windowdispatch(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	char    *s = NULL;
 
	switch(rqstp->rq_proc) {
		case NULLPROC:
			if (!svc_sendreply( transp, xdr_void, NULL))
				fprintf(stderr, "can't reply to RPC call\n");
			return;
		case RENDERSTRING:
			if (!svc_getargs( transp, xdr_wrapstring, &s)) {
				fprintf(stderr, "can't decode arguments\n");
				/* Tell caller an error occurred */
				svcerr_decode(transp);
				break;
			}
			/* Code here to render the string s */
			if (!svc_sendreply( transp, xdr_void, (caddr_t) NULL))
				fprintf( stderr, "can't reply to RPC call\n");
			break;
		case RENDERSTRING_BATCHED:
			if (!svc_getargs(transp, xdr_wrapstring, &s)) {
				fprintf(stderr, "can't decode arguments\n");
				/* Be silent in the face of protocol errors */
				break;
			}
			/* Code here to render string s, but send no reply! */
			break;
		default:
			svcerr_noproc(transp);
			return;
	}
	/* Now free string allocated while decoding arguments */
	svc_freeargs(transp, xdr_wrapstring, &s);
}


Note - To illustrate the benefits of batching, Example 5-4 and Example 5-6 were completed to render the lines in a 25,144-line file. The rendering service throws away the lines. The batched version of the application is four times as fast as the unbatched version.


 
 
 
  Previous   Contents   Next