[comp.unix.wizards] Using select

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]