Page principale | Liste des fichiers | Portée globale

reseau.c

Aller à la documentation de ce fichier.
00001 /*******************************************************************************\
00002  *                      SeaBattle - D.Bernaudeau, J. Vehent - 2006             *
00003  *                                                                             *
00004  *      fonction reseau: établissement des connexions, boucle dans l'envoi     *
00005  *              et la réception des messages, envoi et recoit les tirs         *
00006  *                                                                             *
00007 \*******************************************************************************/ 
00008 
00018 #include <stdio.h>
00019 #include <stdlib.h>
00020 #include <unistd.h>
00021 #include <errno.h>
00022 #include <string.h>
00023 #include <sys/types.h>
00024 #include <sys/socket.h>
00025 #include <netinet/in.h>
00026 #include <arpa/inet.h>
00027 #include <sys/wait.h>
00028 #include <signal.h>
00029 #include <netinet/in.h>
00030 #include <netdb.h>
00031 #include <pthread.h>
00032 #include <ncurses.h>
00033 
00034 #include "reseau.h"
00035 #include "affichage.h"
00036 #include "jeu.h"
00037 
00042 #define PORTECOUTE      1664
00043 
00048 #define MAXDATASIZE     200
00049 
00054 #define MAXCONNECT      1
00055 
00056 /*******VARIABLES GLOBALES*******/
00057 
00062 WINDOW* fenetre_chat;
00063 
00068 char    tab_message[9][200];
00069 
00074 bool quitter=false;
00075 
00080 int socket_fd;
00081 
00082 /*********************************/
00083 
00084 
00086 void config_reseau(){
00087 
00088         int yes=1;
00089         
00090         /* Creation du socket file descriptor local
00091          * sa socket_fd = socket(int domain, int type, int protocol)
00092          * param socket_fd fichier de socket
00093          * param domain entier, specifie le type d'adresse (PF_INET == AF_INET)
00094          * param type entier, specifie le type de socket (STREAM == connection (tcp))
00095          * param protocol entier, specifie le flag de protocol (0 == mode auto)
00096          *
00097          * return -1 en cas d'erreur (testé avec un if), sinon elle renvoie un socket file descriptor
00098          */
00099         if ((socket_fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
00100                         perror("socket");
00101                         exit(EXIT_FAILURE);
00102         }
00103         
00104         /* Modification des parametres d'un socket 
00105          * sa setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
00106          * param s socket file descriptor
00107          * param level au niveau socket, ce paramétre prend la valeur SOL_SOCKET
00108          * param optname option que l'on modifie, ici c'est SO_REUSEADDR pour la reutilisation du socket
00109          * param *optval pointeur vers la nouvelle valeur, ici 1
00110          * param optlen option de longueur, ici la taille d'un int
00111          * return -1 en cas d'erreur (testé dans un if), sinon rien
00112          */
00113         if (setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
00114                 perror("setsockopt");
00115                 exit(EXIT_FAILURE);
00116         }
00117         
00118         
00119 }
00120 
00121 /***************************************************************************************************************/
00122 
00124 
00127 void send_shoot(char *objective){
00128         int lenobj;
00129         lenobj = strlen(objective);
00130         if (send(socket_fd, objective, lenobj, 0) == -1) perror("send");
00131 }
00132 
00133 
00134 /***************************************************************************************************************/
00135 
00137 
00140 void send_rep_shoot(int result){
00141         
00142         char rep[5]="";
00143         
00144         //on convertit l'entier en char pour l'envoyer sur le réseau
00145         sprintf(rep,"%d",result);
00146         
00147         int replen = strlen(rep);
00148         if (send(socket_fd, rep, replen, 0) == -1) perror("send");
00149 }
00150 
00151 
00152 /***************************************************************************************************************/
00153 
00155 void recv_msg(){
00156         int octets_recus;
00157         char buf[MAXDATASIZE]=".", quit[]="/quit", go[]="/go", shoot[]="/shoot", slash[]="/";
00158 
00159         do{
00160                 
00161                 //on place le socket en mode recv pour recevoir les messages
00162                 if ((octets_recus=recv(socket_fd, buf, MAXDATASIZE-1,0)) == -1){
00163                         perror("recv");
00164                         exit(EXIT_FAILURE);
00165                 }
00166                 buf[octets_recus]='\0';
00167                 
00168                 /* ZONE DE CONTROLES DES CHAINES DE CARACTERES
00169                  *
00170                  * avant d'afficher le message reçu comme un texte de conversation, 
00171                  * on vérifie que ce n'est pas un message particulier (débutant par un slash).
00172                  *
00173                  * Si c'est le cas, alors on test le cas du /go, puis celui du /shoot
00174                  * enfin celui du /quit
00175                  *
00176                  * si le message n'est aucun de ceux la, alors on test si c'est la réponse a
00177                  * un tir, sinon on affiche comme du texte
00178                  */
00179                 if (0 == strncmp(buf, slash,1)){
00180                 
00181                         //test si la notification /go est recue
00182                         //si c'est le cas alors l'adversaire et pret : on appel adv_go()
00183                         if (0 == strcmp(buf,go)) adv_go();
00184         
00185                         else{   //test de la reception d'un tir ennemi
00186                                 //on l'envoi directement a la fonction qui le traite
00187                                 if(0 == strncmp(buf,shoot,6)) recv_shoot(buf);
00188 
00189                                 /* On test si on recoit un /quit (fermeture de connection)
00190                                 * si c'est le cas, on place le booleen "quitter" a true pour
00191                                 * sortir du while
00192                                 */
00193                                 else if(0 == strncmp(buf, quit, 5))quitter=true;
00194                         }
00195                         //test si le message est une réponse a un tir
00196                 }else   if (strlen(buf) <= sizeof(int)){
00197                                 rep_shoot(buf);
00198                         }
00199                         //envoi du message dans la fonction qui gére le tableau et l'affichage
00200                         else saisie(tab_message, fenetre_chat, buf);
00201                 
00202         }while( !quitter );
00203 
00204         //quand on quitte la discussion, on affiche un message de fermeture avec un sleep
00205         //(histoire d'avoir le temps de le lire :p)
00206         afficher_erreur("FERMETURE DE LA CONNECTION DEMANDEE, passez une bonne journee ;)");
00207         sleep(2);
00208 }
00209 
00210 
00211 /***************************************************************************************************************/
00212 
00214 void send_msg(){
00215         
00216         char msg[MAXDATASIZE]="", phrase[MAXDATASIZE+11], go[]="/go", quit[]="/quit", slash[]="/";
00217         int len;
00218         //tableau de char pour le nom
00219         char nom[16]; int lennom;
00220 
00221         //saisie du pseudo de l'utilisateur au début de la partie
00222         mvprintw(LINES-5, 1, "Tapez votre pseudo : ");
00223         move(LINES-4,2);
00224         refresh();
00225         getstr(nom);
00226         lennom = strlen(nom);
00227         nom[lennom]='\0';
00228         strcat(nom, "> ");
00229         lennom = strlen(nom);
00230         nom[lennom]='\0';
00231 
00232         //Initialisation de la saisie d'un message
00233         move(LINES-5,1);clrtoeol();
00234         mvprintw(LINES-5,1,"Votre message :");
00235         move(LINES-4,2);clrtoeol();
00236         refresh();
00237         
00238         /* BOUCLE INFINIE TANT QUE L'UTILISATEUR N'A PAS SAISI /QUIT
00239          */
00240         while(strcmp(msg, quit)){
00241                 
00242                 //saisie du message a envoyer
00243                 getstr(msg);
00244                 
00245                 //suppression de la fenetre des messages d'erreur
00246                 effacer_erreur();
00247 
00248                 /* CONTROLE DES CHAINES DE CARACTERES
00249                  *
00250                  * on test si la saisie commence par un slash, si c'est le cas on passe en analyse
00251                  *
00252                  * sinon on envoie le message comme du texte
00253                  */
00254 
00255 
00256                 //le premier caractere est un slash, on analyse la saisie
00257                 if (0 == strncmp(msg,slash,1)){
00258         
00259                         //la chaine saisie est /go
00260                         if(0 == strcmp(msg, go)){
00261                                 len=strlen(msg);
00262                                 
00263                                 //test du message dans la fonction d'interpretation
00264                                 //elle renvoie -2 si le joueur n'a pas placé tous ses bateaux
00265                                 //et donc ne peut pas démarrer
00266                                 if (!(-2 == interpretation(msg))){
00267                                         if (send(socket_fd, msg, len, 0) == -1) perror("send");}
00268                         }
00269                         
00270                         /* Demande de fermeture de connection
00271                          * le message est envoyé tel quel dans la socket
00272                          */
00273                         if(0 == strcmp(msg, quit)){
00274                                 len=strlen(msg);
00275                                 if (send(socket_fd, msg, len, 0) == -1) perror("send");
00276                         }
00277                         else{
00278                                 if( -1 == interpretation(msg) ){
00279                                         afficher_erreur("Commande inconnue ou mal formulée");
00280                                 }
00281                         }
00282 
00283                         //affichage de la saisie dans ncurses
00284                         saisie(tab_message, fenetre_chat, msg);
00285 
00286                 }
00287                 else {
00288                         /* Si l'on est dans ce cas, c'est que le message saisie est une discussion
00289                          * normale, on l'envoi au travers de la socket
00290                          */
00291                         
00292                         //dans le tableau "phrase" on met d'abord le nom de l'emetteur
00293                         strcpy(phrase, nom);
00294                         
00295                         //on met ensuite le texte a envoyer
00296                         strcat(phrase, msg);
00297                 
00298                         //et on calcul de la longueur de la phrase
00299                         len=strlen(phrase);
00300                         
00301                         /* Envoi du message au travers de la socket réseau
00302                          * sa send(int s, const void *buf, size_t len, int flags)
00303                          * param s socket file descriptor
00304                          * param *buf pointeur vers le tableau de caractéres a envoyer
00305                          * param len longueur de la chaine a envoyer
00306                          * param flags drapeau non utilisé
00307                          * return -1 en cas d'erreur (testé dans un if), rien sinon
00308                          */
00309                         if (send(socket_fd, phrase, len, 0) == -1) afficher_erreur("erreur lors de l'envoi du message"); 
00310                                 
00311                         //affichage dans ncurses
00312                         //en local une fois que le send a reussi
00313                         saisie(tab_message, fenetre_chat, phrase);
00314                 }
00315         
00316         }
00317         //quand on sors, on place le booleen quitter a true pour fermer le socket recv
00318         quitter=true;
00319         //sortie du thread une fois la commande /quit saisie
00320         pthread_exit(0);
00321 }       
00322 
00323 
00324 /***************************************************************************************************************/
00325 
00327 
00330 int client(int IP){
00331 
00332         int i;
00333 
00334         /* les structures de parametrages reseau */
00335         struct sockaddr_in my_addr_clt;
00336         struct sockaddr_in their_addr_srv;
00337         /* structure de creation du thread */
00338         pthread_t thread_send_msg;
00339         
00340         /* Initialiser le tabeau des messages
00341          */
00342         for(i=0;i<10;i++){
00343                 strcpy(tab_message[i],"");
00344         }
00345         
00346         /* appel de la fonction de configuration reseau
00347          * on passe l'adresse du socket en parametre
00348          *
00349          * le socket est modifie directement dans la memoire
00350          */
00351         config_reseau();
00352         
00353         
00354         /* my_addr_clt()
00355          * param sin_family domaine d'adresse, octet dans l'ordre de l'hote
00356          * param sin_addr.s_addr adresse source recuperee de l'OS
00357          * param memset mise a \0 de la fin de la structure
00358          *
00359          */
00360         my_addr_clt.sin_family = AF_INET;
00361         my_addr_clt.sin_addr.s_addr = INADDR_ANY;
00362         memset(&(my_addr_clt.sin_zero), '\0', 8);
00363         
00364 
00365         /* their_addr_srv()
00366          * configuration reseau du serveur auquel on se connecte
00367          * le port de destination est converti en ecriture reseau par la fonction
00368          * htons() => host to network short
00369          */
00370         their_addr_srv.sin_family = AF_INET;
00371         their_addr_srv.sin_port = htons(PORTECOUTE);
00372         their_addr_srv.sin_addr.s_addr = inet_addr( (void *)IP);
00373         memset(&(their_addr_srv.sin_zero), '\0', 8);
00374 
00375 
00376         /* connect() la fonction connect renvoie -1 en cas d'echec
00377          * param clt_recv_fd socket file descriptor
00378          * param their_addr_srv pointeur vers la structure struct sockaddr
00379          * param addrlen taille de la structure sockaddr
00380          */
00381         if (connect(socket_fd, (struct sockaddr *)&their_addr_srv,
00382                                 sizeof(struct sockaddr)) == -1){
00383                 perror("connect");
00384                 exit(EXIT_FAILURE);
00385         }
00386 
00387         init_screen();
00388         
00389         //Initialisation de la fenetre de discussion
00390         fenetre_chat = init_chat(tab_message);
00391         
00392         //initialisation des matrices de jeu
00393         init_matrices();
00394 
00395         /* pthread_create() processus parallele pour l'envoi de messages
00396          * param &thread_send_msg structure contenant le thread
00397          * param NULL argument du thread laissee par defaut
00398          * param send_msg fonction executee par le thread (transtypee en void *)
00399          * param socket_fd socket file descriptor passee en argument (transtypee en int *)
00400          */
00401         if( 0!= pthread_create (&thread_send_msg, 
00402                                         NULL, 
00403                                         (void *)send_msg,
00404                                         NULL) ){
00405                 perror("creation du thread");
00406                 exit(EXIT_FAILURE);
00407 
00408         }
00409 
00410         /* pthread_detach() vidage du thread de la memoire des qu'il termine
00411          * param thread_send_msg structure contenant le thread
00412          * cette fonction permet de detacher le thread du programme car on ne 
00413          * cherche pas a recuperer de valeur a la fin de son execution
00414          * il sera donc nettoye automatiquement
00415          */
00416         pthread_detach(thread_send_msg);
00417         
00418         /* le programme continue dans le processus pere 
00419          * appel de la fonction de reception des messages
00420          */
00421         recv_msg();
00422 
00423         //DESTRUCTION DE L'AFFICHAGE NCURSE
00424         delwin(fenetre_chat);
00425         endwin();
00426 
00427         return 0;
00428 
00429 }
00430 
00431 
00432 /***************************************************************************************************************/
00433 
00435 int serveur(){
00436         
00437         /* les structures de parametrages reseau */
00438         struct sockaddr_in my_addr_srv;
00439         struct sockaddr_in their_addr_clt;
00440         /* structure de creation du thread */
00441         pthread_t thread_send_msg;
00442         /*les variables diverses */
00443         int sin_size;
00444 
00445         config_reseau();
00446         
00447         /* my_addr_srv()
00448          * param sin_family domaine d'adresse, octet dans l'ordre de l'hote
00449          * param sin_addr.s_addr adresse source recuperee de l'OS
00450          * param sin_port port source
00451          * param memset mise a 0 de la fin de la structure
00452          */
00453         my_addr_srv.sin_family = AF_INET;
00454         my_addr_srv.sin_addr.s_addr = INADDR_ANY;
00455         my_addr_srv.sin_port = htons(PORTECOUTE);
00456         memset(&(my_addr_srv.sin_zero), '\0', 8);
00457 
00458         
00459         // On attend la connection du client sur le port PORTECOUTE
00460         if (bind(socket_fd, (struct sockaddr *)&my_addr_srv, 
00461                                 sizeof(struct sockaddr))==-1){
00462                 perror("bind");
00463                 exit(1);
00464         }
00465         
00466         fprintf(stdout,"\n\nEn attente de connection................");
00467         
00468         if (listen(socket_fd, MAXCONNECT) == -1) {
00469                 perror("listen");
00470                 exit(1);
00471         }
00472         
00473         /* accept()
00474          * param socket_fd socket file descriptor retournee par la fonction socket()
00475          * param their_addr_clt pointeur vers la structure sockaddr du client
00476          * param sin_size taille de la structure sockaddr
00477          * return -1 en cas d'erreur (testé dans un if), rien sinon
00478          */
00479         sin_size = sizeof(struct sockaddr_in);
00480         
00481         if((socket_fd = accept(socket_fd, (struct sockaddr *)&their_addr_clt,&sin_size))==-1){
00482                 perror("accept");
00483                 exit(1);
00484         }
00485         
00486         // UNE FOIS LA CONNECTION ETABLIT, ON DEMARRE L'AFFICHAGE NCURSE
00487         init_screen();
00488         
00489         fenetre_chat = init_chat(tab_message);
00490                 
00491         //initialisation des matrices de jeu
00492         init_matrices();
00493 
00494         /* pthread_create() processus parallele pour l'envoi de messages
00495          * param &thread_send_msg structure contenant le thread
00496          * param NULL argument du thread laissee par defaut
00497          * param send_msg fonction executee par le thread (transtypee en void *)
00498          * param socket_fd socket file descriptor passee en argument (transtypee en int *)
00499          */
00500         if( 0!= pthread_create (&thread_send_msg, 
00501                                         NULL, 
00502                                         (void *)send_msg,
00503                                         NULL) ){
00504                 perror("creation du thread");
00505                 exit(EXIT_FAILURE);
00506 
00507         }
00508 
00509         /* pthread_detach() vidage du thread de la memoire des qu'il termine
00510          * param thread_send_msg structure contenant le thread
00511          * cette fonction permet de detacher le thread du programme car on ne 
00512          * cherche pas a recuperer de valeur a la fin de son execution
00513          * il sera donc nettoye automatiquement
00514          */
00515         pthread_detach(thread_send_msg);
00516         
00517         //dans le processus pere on recoit les messages et on les affiches
00518         recv_msg();
00519         
00520         //DESTRUCTION DE L'AFFICHAGE NCURSE
00521         delwin(fenetre_chat);
00522         endwin();
00523 
00524         return 0;
00525 }
00526 

Généré le Sun Apr 30 17:46:01 2006 pour SeaBattle par  doxygen 1.4.4