anund@idt.unit.no (Anund Lie) (03/19/91)
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.) On the socket level, this will be detected by a read() on the socket returning 0 (end-of-file). The best thing would be to be able to register a procedure or in some other way control the action to be performed when RPC detects EOF on a socket. (It has to detect EOF anyway and close its end of the socket, hasn't it?) The manuals does not mention this, but /usr/include/rpc/svc.h (SunOS 4.1) defines the operation SVC_STAT(). Presumably the (pointer to) the transport handle could be saved at the first call from a new client, and the server periodically SVC_STAT() the set of active handles. Is this a reasonable thing to do? There does not seem to be any guarantee that transport handles are unique over different RPC calls, so that it is OK to store them in this way. Anund Lie Email: anund@idt.unit.no (Internet)
mh@roger.imsd.contel.com (Mike Hoegeman) (03/23/91)
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 <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; }