#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; //interface de la classe socketClient class sockServeur; class socketClient { public: /***********Les Propriétés*******/ int id; //identifiant de la socket équivalent du handle pour les fichiers sockServeur * destinataire; //Il s'agit de l'adresse de l'instance de la classe sockServeur avec laquelle va communiquer la socket client socketClient * suivante; //socket client suivante dans la liste chaînée des sockets clients avec lesquelles communique le serveur struct sockaddr_in adresse_client; //adresse du client concerné par la socket socklen_t long_adresse_client; //IDEM SOUS FORME D'UN ENTIER string pseudo; //PSOEUDO POUR L'INSTANT INUTILISÉ /********* Les méthodes *********/ void ready(); //ecrit renvoie faux si elle n'a pas pû écrire bool ecrit(string quoi); string lit(int max); socketClient(sockServeur * dest); void setLectureNonBloquante(); void setLectureBloquante(); }; //fin de l'interface de la socket client //interface de la classe sockServeur class sockServeur { public: /************* les propriétés **************************/ string adresse; int port; int id; int nbConnexionMaxi; struct sockaddr_in adresse_serveur; socketClient* premiereSocket; /************ Les méthodes ...........................*/ //Le constructeur de la socket serveur avec des valeurs par défaut //il attend dans l'ordre, l'adresse IP sur laquelle il doit écouter // puis le numéro de port et enfin le nombre maximum de connexion sockServeur(string a,int p,int nbMax); //méthode qui lance lécoute sur le port void ecoute(); //renvoie le nombre de connection au serveur int nbConnectes(); void debug(); //renvoie la liste des pseudos des personnes connectées string listeConnectes(); //supprime une socket client de la liste void vire(socketClient*& sc); //methode qui écrit mess ds ttes les sockets clientes de la liste sauf à l'emmetteur void divulgue(string mess, socketClient* sauf); }; //fin de l'interface de classe socket serveur //fonction de traitement des sockets clients //c'est ici que vous implémentez votre protocole void * traiteConnexion(void * sc) { //début de l'implémentation du serveur socketClient * socketCourante =(socketClient *)sc; sockServeur * laSocketDuServeur=socketCourante->destinataire; //la lecture de la socket est bloquante: le thread attendra jusqu'à ce qu'il reçoive quelque chose socketCourante->setLectureBloquante(); while(true) { string messageClient; //lecture éventuelle d'un message(la lecture est bloquante) messageClient= socketCourante->lit(255); if(messageClient=="ilEstParti") { printf("il y en a un qui s'est sauvé sans quitter\n"); //demander au serveur de virer la socketCourante laSocketDuServeur->vire((socketClient*&)sc); //on met fin au thread pthread_exit(0); } else { if (messageClient.length()>2) { printf(messageClient.c_str()); //on roupille une seconde pour soulager le processeur //si il y a un message et qu'il n'est pas egal à "\r\n" //si c'est la commande pour quitter string quitter="q\r\n"; string user="user:"; string hote="hote:"; if(messageClient==quitter) { printf("il y en a un qui se sauve\n"); //demander au serveur de virer la socketCourante laSocketDuServeur->vire((socketClient*&)sc); //on met fin au thread pthread_exit(0); } else { if(messageClient.length()>5 && string(messageClient,0,5)==user) { socketCourante->pseudo=string(messageClient,5,messageClient.length()-7); } else { //un message a été tapé, il faut le divulguer //on va d'abord le nettoyer enlever les caractères de fin string messageOk=string(messageClient,0,messageClient.length()-2); //envoi du message à toutes les sockets clients sauf à socketCourante (socketCourante->destinataire)->divulgue(socketCourante->pseudo+":>"+messageOk,socketCourante); } } } } } } //implémentation de la classe socketServeur //Le constructeur de la socket serveur avec des valeurs par défaut //il attend dans l'ordre, l'adresse IP sur laquelle il doit écouter // puis le numéro de port et enfin le nombre maximum de connexion sockServeur::sockServeur(string a="127.0.0.1",int p=9995,int nbMax=5) { id=socket(AF_INET, SOCK_STREAM,0); adresse_serveur.sin_family= AF_INET; adresse=a; port=p; adresse_serveur.sin_addr.s_addr= htonl(INADDR_ANY); adresse_serveur.sin_port= htons(port); int long_serveur=sizeof(adresse_serveur); if(bind(id,(struct sockaddr *)&adresse_serveur,long_serveur)!=-1) { listen(id,nbMax); printf("[OK] Le serveur de chat est lancé\n"); premiereSocket=0; ecoute(); } else { printf("[ERREUR] désolé le port est occupé\n"); return; } } //notre serveur écoute (il crée une instance de la classe socketClient qui attend le client void sockServeur::ecoute() { printf("le serveur écoute\n"); //boucle sans fin puisque démon while(true) { //point de blocage premiereSocket =new socketClient(this); premiereSocket->ready(); //on ne passera cette ligne que lorsque quelqu'un se connecte //création d'une tâche indépendante le thread qui va exécuter la fonction traiteConnexion pthread_t un_thread; int res =pthread_create(&un_thread,NULL,&traiteConnexion,(void *)premiereSocket); } } int sockServeur::nbConnectes() { socketClient * courante=premiereSocket->suivante; int nb=0; while(courante) { nb++; courante=courante->suivante; } return(nb); } /***************** fournit sous forme de chaîne la liste des connectés ***************************/ /***************** statut: testée pas de bug ***************************/ string sockServeur::listeConnectes() { socketClient * courante=premiereSocket->suivante; string liste="Sont présents:\n"; while(courante) { liste+=courante->pseudo+"\n"; courante=courante->suivante; } return(liste); } void sockServeur::debug() { socketClient * courante=premiereSocket; printf("liste:"); while(courante) { printf("%d->",courante); courante=courante->suivante; } printf("0\n"); } void sockServeur::divulgue(string mess, socketClient* sauf) { socketClient * courante=premiereSocket->suivante; bool leClientEstToujoursLa; while(courante) { socketClient* memo =courante->suivante; if (courante!=sauf) { leClientEstToujoursLa=courante->ecrit(mess); //si le client n'existe plus on le retire de la liste des sockets clientes à gérer //if(! leClientEstToujoursLa) vire(courante); } //on passe au client suivante courante=memo; } } void sockServeur::vire(socketClient *& sc) { //memorisation du fd de la socket int fdAVirer=sc->id; socketClient * prec=0; socketClient * courante=premiereSocket; while (courante!=sc) { prec=courante; courante=courante->suivante; } prec->suivante=sc->suivante; //libération de la mémoire associée à la socket delete (sc); //raz de sc sc=0; //fermeture du fichier associé à la socket close(fdAVirer); } //fin de l'implémentation de classe socket serveur //début de l'implémentation des méthodes de la classe socketClient //cette méthode permet d'envoyer une chaîne DE CARACTÈRES au client bool socketClient::ecrit(string quoi) { int resultat=write(id,quoi.c_str(),quoi.length()); printf("resultat vaut %d \n",resultat); if(resultat>0) return true; else return false; } //cette méthode permet la réception d'une chaîne du client string socketClient::lit(int max) { char * mot; mot=(char *) malloc(max); int nbChar=read(id,mot,max); if(nbChar>0) { mot[nbChar]='\0'; string chaineLue=mot; return (chaineLue); } else return("ilEstParti"); } //le constructeur: remarquez que la socket client a besoin de connaitre la socket serveur socketClient::socketClient(sockServeur * dest) { destinataire=dest; suivante=dest->premiereSocket; pseudo=""; } void socketClient::ready() { id=accept(destinataire->id,(struct sockaddr *)&adresse_client,&long_adresse_client); } void socketClient::setLectureBloquante() { fcntl(id,F_SETFL,O_RDWR); } void socketClient::setLectureNonBloquante() { fcntl(id,F_SETFL,O_NONBLOCK|fcntl(id,F_GETFL,0)); } //fin de l'implémentation de la classe socketClient //programme principal Vachement compliqué int main() { //on instancie la classe sockserveur sockServeur SockServ; //on aurait pû appeler le constructeur avec d'autres valeurs que celles par défaut //ex sockserveur SockServ("144.124.10.1",3456,25); return(0); } //enfin vla comment faut compiler //g++ -DREENTRANT servmot.cpp -o pressePapReseau -lpthread //ne pas oublier de lier à la biblio pthread // et ne pas oublier non plus l'option -DREENTRANT