/* * 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 #include #include #include #include #include #include #include #include #include /* 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 */