/*
 * select_socket.c
 * This program demonstrates how to use the select function.
 * Note that although we did not cover sockets in this book, 
 * the code will remain the same for file descriptors. To use 
 * this program. Open several xterms, run the program, and then
 * telnet to any of the ports. Once the telnet session is connected
 * type anything and it will echo back. For example:
 *
 * From xterm 1:
 *
 * bash$ ./select_socket
 *
 * From xterm 2:
 *
 * bash$ telnet localhost 8888
 *
 * From xterm 3:
 *
 * bash$ telnet localhost 8889
 *
 * to compile: gcc -O -pipe  select_socket.c  -o select_socket
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/time.h>


/* our defines */
#define PORT             (8888)
#define MAXBUFF          (1024)
#define MAX_CONN         (16)
#define TIMEOUT          (1024)
#define MY_MAX(a,b)      (a = (a > b) ? a : b )
#define SELECT_ERR       (-1)
#define SELECT_EXPIRE    (0)

int main(int argc, char **argv)
{
	int i, j, max = 0, sfds[MAX_CONN], afd;
	size_t len;
	fd_set list;
	char buff[MAXBUFF];
	struct sockaddr_in sock[MAX_CONN];
	struct timeval tm;

	/* initialize our buffer */
	memset(buff, 0, MAXBUFF);

	/*
	 * We will loop through each file descriptor. First
	 * we will create a socket bind to it and then call 
	 * listen on it. If we get an error we just exit, 
	 * which is fine for demo code, but not good in the
	 * real world where errors should be handled properly. 
	 */
	for( i = 0; i < MAX_CONN; i++ )
	{
		/* check to see that we can create them */
		if( (sfds[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
		{
			perror("Cannot create socket");
			exit(1);
		}

		/* now fill out our socket stuctures */
		memset(&sock[i], 0, sizeof(struct sockaddr_in));
		sock[i].sin_family = AF_INET;
		sock[i].sin_port = htons(PORT + i);
		len = INADDR_ANY;
		memset(&sock[i].sin_addr, len, sizeof(struct in_addr));

		/* Now bind to the socket	*/
		if( bind(sfds[i], (struct sockaddr *) &sock[i], sizeof(struct sockaddr_in)) < 0 )
		{
			perror("Cannot bind to the socket");
			exit(1);
		}

		/* set our options */
		if( setsockopt(sfds[i], SOL_SOCKET, SO_REUSEADDR, &j, sizeof(int)) < 0 )
		{
			perror("Cannot set socket options \n");
		}

		/* set the socket into the listen state */
		if( listen(sfds[i], 5) < 0 )
		{
			perror("Failed to listen on the socket \n");
		}

	}/* for */

	/* Create our timeout structure */
	tm.tv_sec = TIMEOUT;
	tm.tv_usec = 0;     

	/*
	 * Our main loop. Note that we will have to re-initialize
	 * and re-add the descriptors to the fd_set structure
	 * each time we loop around. This is because select will 
	 * modify values inside the fd_set structure. Therefore, 
	 * the fd_set structure is no longer the same structure as 
	 * when we first passed it to select.  
	 */
	while( 1 )
	{
		max = 0;
		/* Always clear the fd_set structure before using it ! */
		FD_ZERO(&list);
		for( i =0; i < MAX_CONN; i++ )
		{
			FD_SET(sfds[i], &list);
			max = MY_MAX(max, sfds[i] );
		}
		max++;

		/*
		 * Now call select. Note, when select returns we have three possibilities:
		 * I)   The timeout has expired
		 * II)  Select had an error
		 * III) We have a socket ready to accept
		 */
		j = select(max, &list, NULL, NULL, &tm);
		switch( j )
		{
			case SELECT_EXPIRE:
				printf("Timeout has expired !\n");
				break;                                                    

			case SELECT_ERR:
				perror("Error on select");

			default:   
				/* 
				 * Now we have to loop through each descriptors to
				 * see which is ready to accept. To do this we must
				 * use the FD_ISSET macro.
				 */
				for( i =0; i < MAX_CONN; i++ )
				{
					if( FD_ISSET(sfds[i], &list) )
					{
						/*
						 * We now have to accept the connection and then
						 * echo back what is written.
						 */
						printf("We have a connection \n");
						len = sizeof(struct sockaddr_in);
						afd = accept(sfds[i], (struct sockaddr *)&sock[i], &len);
						len = read(afd, buff, MAXBUFF);
						write(afd, buff, len +1);
						printf("Echoing back:\n %s \n", buff);
						close(afd);
					}

				} /* for */

		} /* switch */

	}/* while(1) */

	/* FIN */
	return(0);

} /* main */







syntax highlighted by Code2HTML, v. 0.9