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