RPC server -- How to detect death of client? -- (a tad long)

Mike Hoegeman mh at roger.imsd.contel.com
Sat Mar 23 06:20:20 AEST 1991


In article <1972 at brchh104.bnr.ca> anund at idt.unit.no (Anund Lie) writes:
>When running Sun RPC over TCP (or other connection-oriented services, for
>that matter), how can the server detect that a client process has died?
>(More precisely: That no process has the other end of the connection
>open.)  

I've seen a couple of questions like this so i'll post below an example of
how I deal with client management in a tcp based RPC service. The comments
explain things fairly well but if anyone has any questions go ahead and
drop me a line

mike hoegeman, mh at awds.imsd.contel.com

------ example below -------

#include <stdio.h>
#include <rpc/rpc.h>
#include "db.h" /* your service.h goes here */

#define LogM fprintf
#define AS_ERROR stderr
#define AS_DEBUG stderr
#define LogInit(X) 

#include <sys/errno.h>

extern void db_program_1();

SVCXPRT *transp;

main()
{
    LogInit();
    (void) pmap_unset(DB_PROGRAM, DB_VERSION);

    if (!svc_register(transp,
	DB_PROGRAM, DB_VERSION, db_program_1, IPPROTO_UDP))
    {
	fprintf(stderr, "unable to register (DB_PROGRAM, DB_VERSION, udp).\n");
	exit(1);
    }
    transp = svctcp_create(RPC_ANYSOCK, 0, 0);
    if (transp == NULL)
    {
	(void) fprintf(stderr, "cannot create tcp service.\n");
	exit(1);
    }
    if (!svc_register(transp,
	DB_PROGRAM, DB_VERSION, db_program_1, IPPROTO_TCP))
    {
	fprintf(stderr, "unable to register (DB_PROGRAM, DB_VERSION, tcp).\n");
	exit(1);
    }
    run(); /* like svc_run() but does client management callbacks */
    (void) fprintf(stderr, "run returned\n");
    exit(1);
}


run()
{
    extern int errno;

#ifdef FD_SETSIZE
    fd_set  readfds;
    static fd_set  oldfds;

    oldfds = svc_fdset;
#else
    int     readfds;
    static int     oldfds;

    oldfds = svc_fds;
#endif	/* def FD_SETSIZE */


    for (;;)
    {
	register int setsize;
	setsize = _rpc_dtablesize();

#ifdef FD_SETSIZE
	readfds = svc_fdset;
#else
	readfds = svc_fds;
#endif	/* def FD_SETSIZE */

	switch (select(setsize, &readfds, (int *) 0, (int *) 0,
	    (struct timeval *) 0))
	{
	case -1:
	    if (errno == EINTR)
		continue;
	    perror("svc_run: - select failed");
	    return;
	case 0:
	    continue;
	default:
	    {
		/*
		   at the start of each go round in the select loop we can
		   detect when a new connection has been made or broken this is
		   were we do our client management
		*/

		register int chunk;
		register int bit;
		register unsigned long mask;
		register unsigned long *maskp;
		int fd;

		/* for each bit in the new mask */
		maskp = (unsigned long *) (svc_fdset.fds_bits);
		for (chunk = 0; chunk < setsize; chunk += NFDBITS)
		    for (mask = *maskp++;
			bit = ffs(mask); mask ^= (1 << (bit - 1)))
		    {
			fd = chunk+bit-1;
			/*
			   if fd is not in the old mask, then we have a
			   new connection
			*/

			if (FD_ISSET(fd, &svc_fdset) && !FD_ISSET(fd, &oldfds))
			    if (c_add(fd))
				LogM(AS_ERROR, "add error");
		    }

		/* for each bit in the old mask... */
		maskp = (unsigned long *) (oldfds.fds_bits);
		for (chunk = 0; chunk < setsize; chunk += NFDBITS)
		    for (mask = *maskp++;
			bit = ffs(mask); mask ^= (1 << (bit - 1)))
		    {
			fd = chunk+bit-1;
			/* 
			    if fd is not in the new mask then we have lost
			    a connection
			*/
			if (FD_ISSET(fd, &oldfds) && !FD_ISSET(fd, &svc_fdset))
			    if (c_delete(fd))
				LogM(AS_ERROR, "delete error");
		    }

		oldfds = svc_fdset; /* should have a better scheme for
					copying fd sets here */

		svc_getreqset(&readfds);
	    }
	}
    }
}

/* client 'close' callback */
c_delete(n)
int n;
{
    LogM(AS_DEBUG,"c_delete: %d <-----\n", n);
    return 0;
}

/* client 'open' callback */
c_add(n)
int n;
{
    LogM(AS_DEBUG,"c_add: %d <-----\n", n);
    return 0;
}



More information about the Comp.sys.sun mailing list