adjg@otc.OZ (Andrew Gollan) (11/25/86)
I need to have a server that forms a junction between two client processes. Further if one of the clients is not present the other must still be serviced. I read the manual on accept(2) and found that one could use select(2) to wait for incoming connections. I wrote two programs of which the following are the pertinent exerpts. Assume that the 'name' variables are all legal pathnames. ------------------------------------------------------------------------ /* SERVER */ static int mkdlink(name) char *name; { register int s; sockaddr_un sun; unlink(name); sun.sun_family = AF_UNIX; strcpy(sun.sun_path, name); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -1; if (bind(s, (sockaddr *)&sun, strlen(sun.sun_path) + sizeof (short)) < 0) return -1; return s; } main() { register int s0; register int s1; register int accepted = 0; s0 = mkdlink((char *)name1); s1 = mkdlink((char *)name2); (void)listen(s0, 1); (void)listen(s1, 1); for (;;) { int mask = (1 << s0) | (1 << s1); register int i; if (select(2, &mask, (int *)0, (int *)0, 0) < 0) exit(1); for (i = 0; i < NOFILE; ++i) { if ((mask & (1 << i)) == 0) continue; if (accepted & (1 << i)) { register int other; register int cnt; char buf[1024]; if (i == s0) other = s1; else other = s0; if ( (cnt = read(i, buf, sizeof (buf))) > 0 && (accepted & (1 << other)) ) (void)write(other, buf, cnt); } else { register int ns; sockaddr from; int fromlen = sizeof from; if ((ns = accept(i, &from, &fromlen)) < 0) exit(1); close(i); if (i == s0) s0 = ns; else s1 = ns; accepted |= (1 << ns); } } } } ------------------------------------------------------------------------- /* CLIENT */ static int mkdlink(name) char *name; { register int s; sockaddr_un sun; sun.sun_family = AF_UNIX; strcpy(sun.sun_path, name); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return -1; if (connect(s, (sockaddr *)&sun, strlen(sun.sun_path) + sizeof (short)) < 0) return -1; return s; } main() { register int s0; register int cnt; int haveread = 0; char buf[1024]; s0 = mkdlink((char *)name1); for (;;) { int mask = (1 << 0) | (1 << s0); if (select(2, &mask, 0, 0, 0) < 0) exit(1); for (register int i = 0; i < 32; ++i) { if ((mask & (1 << i)) == 0) continue; if ((cnt = read(i, buf, sizeof (buf))) < 0) { exit(1); } if (cnt == 0) { if (haveread) exit(0); continue; } haveread = 1; if (i == 0) (void)write(s0, buf, cnt); else (void)write(1, buf, cnt); } } } ------------------------------------------------------------------------ The problem: The select in the server never returns. The connects in the the client return immediately. It does not do very much useful. I was told that there were lots of bugs in the AF_UNIX domain, so I rewrote the code for the AF_INET domain. Same problem. I put a real live accept in the server for s0 before the select and it worked. The questions: Am I doing something horribly wrong or is it that select(2) does not perform as documented? Have I missed something in the documentation? Andrew Gollan UUCP: {seismo,mcvax}!otc.oz!adjg ACSnet: adjg@otc.oz Overseas Telecommunications Commission
mouse@mcgill-vision.UUCP (der Mouse) (12/04/86)
In article <46@otc.OZ>, adjg@otc.OZ (Andrew Gollan) writes: > I need to have a server that forms a junction between two client > processes. Further if one of the clients is not present the other > must still be serviced. I read the manual on accept(2) and found > that one could use select(2) to wait for incoming connections. Or at least that one is supposed to be able to do so. In this case, luckily, the documentation isn't lying. > I wrote two programs of which the following are the pertinent > ex[c]erpts. [much edited] > /* SERVER */ [...] > main() > { [...] > for (;;) > { > int mask = (1 << s0) | (1 << s1); > register int i; > if (select(2, &mask, (int *)0, (int *)0, 0) < 0) [ The last zero in the call should be (struct timeval *)0. Your care in casting the other zeros to (int *) is commendable, but carry it the rest of the way! ] > exit(1); [...] > ------------------------------------------------------------------------ > The problem: > The select in the server never returns. The connects in the > client return immediately. [...] I was told that there were > lots of bugs in the AF_UNIX domain, so I rewrote the code for > the AF_INET domain. Same problem. > The questions: > Am I doing something horribly wrong or is it that select(2) > does not perform as documented? Have I missed something in the > documentation? You don't state what sort of system it is, but since it has select() I assume 4.2 or 4.3 BSD. Since you are using ints rather than fd_sets for the select() arguments, I assume 4.2 BSD. You missed (or misunderstood) something in the documentation for select(). Here are the relevant excerpts from the 4.3 man page; the meaning applies to 4.2 as well, though the wording may be different: SELECT(2) UNIX Programmer's Manual SELECT(2) nfound = select(nfds, readfds, writefds, exceptfds, timeout) DESCRIPTION [...] The first nfds descriptors are checked in each set; i.e. the descriptors from 0 through nfds-1 in the descriptor sets are examined. That is, your specification of 2 means that file descriptors 0 and 1 are the only ones select() is paying any attention to. Unless s0 and s1 are zero and one, this means that at least one of them is being ignored. You should be passing at least one more than the larger of s0 and s1: int mask = (1 << s0) | (1 << s1); int maxs = (s0 < s1) ? s1 : s0; if (select(maxs+1,&mask,(int *)0,(int *)0,(struct timeval *)0) < 0) der Mouse USA: {ihnp4,decvax,akgua,utzoo,etc}!utcsri!mcgill-vision!mouse think!mosart!mcgill-vision!mouse Europe: mcvax!decvax!utcsri!mcgill-vision!mouse ARPAnet: think!mosart!mcgill-vision!mouse@harvard.harvard.edu [USA NSA food: terrorist, cryptography, DES, drugs, CIA, secret, decode]