Path: utzoo!utgpu!cunews!bnrgate!brtph3!brchh104!brchs1!bnr.ca!rice.edu!sun-spots-request From: mh@roger.imsd.contel.com (Mike Hoegeman) Newsgroups: comp.sys.sun Subject: Re: RPC server -- How to detect death of client? -- (a tad long) Keywords: Networks Message-ID: <2069@brchh104.bnr.ca> Date: 22 Mar 91 20:20:20 GMT Sender: news@brchh104.bnr.ca Organization: Sun-Spots Lines: 168 Approved: Sun-Spots@rice.edu X-Original-Date: Wed, 20 Mar 91 20:30:44 GMT X-Refs: Original: v10n57 X-Sun-Spots-Digest: Volume 10, Issue 52, message 9 X-Note: Submissions: sun-spots@rice.edu, Admin: sun-spots-request@rice.edu In article <1972@brchh104.bnr.ca> anund@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@awds.imsd.contel.com ------ example below ------- #include #include #include "db.h" /* your service.h goes here */ #define LogM fprintf #define AS_ERROR stderr #define AS_DEBUG stderr #define LogInit(X) #include 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; }