///////////////////////////////////////////////////////////////////////////// // Creation 09/12/2003 // // // SELECT.C // -------- // // // Sylvain MARECHAL - sylvain.marechal1@libertysurf.fr ///////////////////////////////////////////////////////////////////////////// // // Utilisation de select() // Ce programme est un mini serveur Web basé sur des sockets bloquantes // avec select() // Il peut y avoir MAX_CLIENT clients de connectes // // Fonctionne sous Win32 et Unix // ///////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 #include #define SOCKET_ERRNO WSAGetLastError() #else #include #include #include #include #include #include #include #define closesocket close #define SOCKET int #define SOCKET_ERRNO errno #endif #include #define MAX_CLIENT 2 ///////////////////////////////////////////////////////////////////////////// // // MAIN ///////////////////////////////////////////////////////////////////////////// // // DESCRIPTION // --main-- // ARGUMENTS // Argument1: int argc // Argument2: char * argv[] // RETOUR/RESULTAT // int // REMARQUE // Rev 09/12/2003 ////////////////////////////////////////////////////////////////////////////// int main( int argc, char * argv[] ) { SOCKET hListen; SOCKET ahSocket[MAX_CLIENT]; struct sockaddr_in addr; int len = sizeof(addr); int nPort = 8000, i; #ifdef _WIN32 // Initialize winsock WSADATA stack_info ; WSAStartup(MAKEWORD(2,0), &stack_info) ; #endif // on recupere le port si donne if( argc != 2 ) { printf( "Usage: <%s> \n", argv[0] ); nPort = 8000; } else { nPort = atoi(argv[1]); } // Affichage if( nPort == 0 ) nPort = 8000; printf( "Listening port %d\n", nPort ); // Listen port nPort hListen = socket( PF_INET, SOCK_STREAM, 0 ); if( hListen == INVALID_SOCKET ) { printf( "socket() error %d\n", SOCKET_ERRNO ); exit(1); } addr.sin_family = AF_INET ; addr.sin_addr.s_addr = htonl (INADDR_ANY); addr.sin_port = htons ((unsigned short)nPort ); if ( bind( hListen, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR ) { printf( "bind() error %d\n", SOCKET_ERRNO ); exit(1); } if ( listen( hListen, 100) == SOCKET_ERROR ) { printf( "listen() error %d\n", SOCKET_ERRNO ); exit(1); } // On initialise le tableau for( i = 0; i < MAX_CLIENT; i ++ ) ahSocket[i] = INVALID_SOCKET; // Boucle du select() // -On permet 10 connexions simultanee while( 1 ) { int i; SOCKET hi_fd; fd_set mask; int retselect; struct timeval timeout; int minTimeout; // On commence par resetter le mask FD_ZERO(&mask ); // On boucle sur les clients connectes for( i = 0; i < MAX_CLIENT; i++ ) { // On demande a etre notifie pour la lecture et la deconnexion if( ahSocket[i] != INVALID_SOCKET ) { FD_SET( ahSocket[i], &mask); if( hi_fd < ahSocket[i] ) hi_fd = ahSocket[i]; } } // On met la socket du listen FD_SET( hListen, &mask); if( (int)hi_fd < hListen ) hi_fd = hListen; // On met un timeout juste pour montrer comment ca marche minTimeout = 5000; // 5000 millisecondes timeout.tv_sec = minTimeout/1000; timeout.tv_usec = (minTimeout%1000)*1000; // On sort du select quand il se passe quelquechose // Ici, on utilise uniquement le mask read. // Le mask write (3° param) sert a etre notifie qu'une // ecriture est possible // Le mask except (4° param) est utile pour la gestion des // erreurs de connexion retselect = select (hi_fd+1, &mask, NULL, NULL, &timeout ); if( retselect == SOCKET_ERROR ) { printf( "select() error %d\n", SOCKET_ERRNO ); exit(1); } else if( retselect == 0 ) { printf( "select() timeout\n" ); } // On reagrade ce qu'il s'est passe for( i = 0; i < MAX_CLIENT; i ++ ) { // Y a t-il de la donnee a lire if( ahSocket[i] != INVALID_SOCKET && FD_ISSET( ahSocket[i], &mask ) ) { int nRet; char Temp[10000]; // On suppose que l'on lit toute l'entete du premier coup // Si ce n'est pas le k, ca va bogguer, car le navigateur // va recevoir 2 reponses pour 1 requete nRet = recv( ahSocket[i], Temp, sizeof(Temp) - 1, 0 ); if( nRet <= 0 ) { printf( "recv() error (connection closed by client?) %d\n", SOCKET_ERRNO ); closesocket( ahSocket[i] ); ahSocket[i] = INVALID_SOCKET; } else { char pHeader[256]; int cbHeader; char pBody[] = "\r\nCOUCOU\r\n\r\n"; // On affiche la requete Temp[nRet] = 0; printf( "Request: '%s'\n", Temp ); // Pour ne pas couper la connexion avec le client (IE/NS), // on lui fournit le content-length cbHeader = sprintf( pHeader, "HTTP/1.0 200 OK\r\n" "Server: My Simple Server\r\n" "Content-Length: %d\r\n" "Content-Type: text/html\r\n" "Connection: Keep-Alive\r\n" "\r\n", sizeof(pBody) - 1 // Taille du composant ); // Send header nRet = send( ahSocket[i], pHeader, cbHeader, 0 ); if( nRet == SOCKET_ERROR ) { printf( "send() error %d\n", SOCKET_ERRNO ); closesocket( ahSocket[i] ); ahSocket[i] = INVALID_SOCKET; } // Send body if( ahSocket[i] != INVALID_SOCKET ) { nRet = send( ahSocket[i], pBody, sizeof(pBody) - 1, 0 ); if( nRet == SOCKET_ERROR ) { closesocket( ahSocket[i] ); ahSocket[i] = INVALID_SOCKET; } } } } } // Y a t-il un nouveau client ? if( FD_ISSET( hListen, &mask ) ) { SOCKET hAccept = accept(hListen, NULL, NULL); // Messsage printf( "Accept incomming connection\n" ); for( i = 0; i < MAX_CLIENT; i ++ ) { if( ahSocket[i] == INVALID_SOCKET ) { ahSocket[i] = hAccept; break; } } // Si le tableau est plein, tant pis if( i == MAX_CLIENT ) { printf( "There is %d clients connected, closing connection\n", MAX_CLIENT ); closesocket( hAccept ); } } } closesocket( hListen ); }