of 21

PROGRAMMATION RÉSEAU. Arnaud Sangnier E/S non bloquantes en C

0 views
All materials on our website are shared by users. If you have any questions about copyright issues, please report us to resolve them. We are always happy to assist you.
Share
Description
PROGRAMMATION RÉSEAU Arnaud Sangnier E/S non bloquantes en C Attente multiple CLIENT 1 SERVEUR Comment faire pour qu'un serveur attende plusieurs messages de clients?
Transcript
PROGRAMMATION RÉSEAU Arnaud Sangnier E/S non bloquantes en C Attente multiple CLIENT 1 SERVEUR Comment faire pour qu'un serveur attende plusieurs messages de clients? CLIENT 2 PR - E/S NON BLOQUANT EN C 2 E/S Bloquantes Comme en Java, un programme en C peut aussi bloquer Par exemple : Un programme qui fait recv et jamais de données n'arrive Exemple de fonctions qui bloquent : recv(), recvfrom(), accept() En fait, lorsque l'on crée une socket avec socket(), elle est déclarée bloquante par défaut Si on veut déclarer une socket comme non bloquante, on peut utiliser la fonction fcntl de la façon suivante : fcntl( sock, F_SETFL, O_NONBLOCK) sock ici est la socket F_SETFL et O_NONBLOCK dit de mettre la socket en mode non-bloquant PR - E/S NON BLOQUANT EN C 3 Problème Prenons un programme qui attend des messages UDP sur deux ports différents et qui les affichent Comment savoir sur quel port attendre d'abord les messages Si on attend d'abord sur le premier port puis ensuite sur le deuxième port et qu'aucun message n'est envoyé sur le premier port alors on a un problème La solution inverse qui consiste à attendre sur le deuxième port d'abord a le même inconvénient PR - E/S NON BLOQUANT EN C 4 Quelles solutions? (1) On peut faire une solution multithreadé On crée deux threads Le premier thread attend une donnée sur le port 1 Le deuxième thread attend une donnée sur le port 2 Le parallélisme résout ainsi le problème de blocage que l'on avait avant Avantages : On sait déjà le faire Inconvénients : On repousse le problème à gérer correctement la concurrence PR - E/S NON BLOQUANT EN C 5 Exemple - Récepteur void *affiche(void *arg){ char *s=(char *)arg; printf( message du thread : %s\n ,s); char *mess=(char *)malloc(sizeof(char)*100); strcpy(mess,s); strcat(mess, fini! ); pthread_exit(mess); int main(){ pthread_t th1,th2; char *s1= je suis le thread 1 ; char *s2= je suis le thread 2 ; pthread_create(&th1,null,affiche,s1); pthread_create(&th2,null,affiche,s2); char *ret1; char *ret2; pthread_join(th1,(void **)&ret1); pthread_join(th2,(void **)&ret2); printf( message recu : %s\n ,ret1); printf( message recu : %s\n ,ret2); return 0; PR - E/S NON BLOQUANT EN C 6 Exemple - ÉMETTEUR int main(int argc,char *argv[]) { if(argc 1){ int sock=socket(pf_inet,sock_dgram,0); struct addrinfo *first_info; struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; hints.ai_socktype=sock_dgram; int r=getaddrinfo( localhost ,argv[1],&hints,&first_info); if(r==0){ if(first_info!=null){ struct sockaddr *saddr=first_info- ai_addr; char tampon[100]; int i=0; for(i=0;i =3;i++){ strcpy(tampon, message ); strcat(tampon,argv[1]); strcat(tampon, ); char entier[3]; sprintf(entier, %d ,i); strcat(tampon,entier); sendto(sock,tampon,strlen(tampon),0,saddr,(socklen_t)sizeof(struct sockaddr_in)); sleep(1); PR - E/S NON BLOQUANT EN C 7 return 0; Quelles solutions? (2) Une autre solution consiste à supprimer la concurrence et à rendre les deux sockets UDP sur lesquels on attend les messages non-bloquantes fcntl( sock1, F_SETFL, O_NONBLOCK); fcntl( sock2, F_SETFL, O_NONBLOCK);... rec1=recv(sock1,tampon,100,0); printf( taille de données recues %d\n ,rec1); if(rec1 =0){ tampon[rec1]='\0'; printf( message recu : %s\n ,tampon); rec2=recv(sock2,tampon,100,0); printf( taille de données recues %d\n ,rec2); if(rec2 =0){ tampon[rec2]='\0'; printf( message recu : %s\n ,tampon); Le problème est que lorsque l'on fait fcntl( sock, F_SETFL, O_NONBLOCK) et qu'on fait recv sur sock. si il n'y a pas de messages, le recv renvoie -1 PR - E/S NON BLOQUANT EN C 8 Exemple - Récepteur non satisfaisant int main() { int sock1=socket(pf_inet,sock_dgram,0); struct sockaddr_in address_sock1; address_sock1.sin_family=af_inet; address_sock1.sin_port=htons(5555); address_sock1.sin_addr.s_addr=htonl(inaddr_any); int sock2=socket(pf_inet,sock_dgram,0); struct sockaddr_in address_sock2; address_sock2.sin_family=af_inet; address_sock2.sin_port=htons(5556); address_sock2.sin_addr.s_addr=htonl(inaddr_any); int r=bind(sock1,(struct sockaddr *)&address_sock1,sizeof(struct sockaddr_in)); if(r==0){ int r2=bind(sock2,(struct sockaddr *)&address_sock2,sizeof(struct sockaddr_in)); if(r2==0){ fcntl( sock1, F_SETFL, O_NONBLOCK); fcntl( sock2, F_SETFL, O_NONBLOCK); char tampon[100]; int rec1=0; int rec2=0; while(1){ rec1=recv(sock1,tampon,100,0); printf( taille de données recues %d\n ,rec1); if(rec1 =0){ tampon[rec1]='\0'; printf( message recu : %s\n ,tampon); rec2=recv(sock2,tampon,100,0); printf( taille de données recues %d\n ,rec2); if(rec2 =0){ tampon[rec2]='\0'; printf( message recu : %s\n ,tampon); return 0; PR - E/S NON BLOQUANT EN C 9 Comment lire au bon moment? Dans la solution précédente, le problème est que Effectivement on ne bloque plus MAIS on teste trop souvent si des données sont disponibles On parle d' ATTENTE ACTIVE On voudrait pouvoir bloquer si il n'y a aucun message à recevoir et être débloqué dès qu'un message arrive sur l'une des deux sockets Comment peut-on faire cela? On va utiliser la fonction select Cette fonction demande au système de réveiller un processus dès qu'une opération sera possible sur un descripteur parmi d'autres Cette fonction permet d'attendre sur tout type de descripteur sockets, tubes, tty etc PR - E/S NON BLOQUANT EN C 10 La fonction select int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); les fd_set sont des ensembles de file descriptor numfds est le numéro maximal du file descriptor que l'on souhaite observer plus 1 readfds est l'ensemble des descripteurs que l'on observe en lecture writefds est l'ensemble des descripteurs sur lesquels on attend de pouvoir écrire exceptfds est l'ensemble des descripteurs surveiller pour conditions exceptionnelles timeout est un temps maximal d'attente, mis à NULL si on ne veut pas de timout Le select est bloquant et renvoie le nombre de descripteurs sur lequel on peut intervenir PR - E/S NON BLOQUANT EN C 11 Exemple typique de select On veut écouter sur deux descripteurs d1 et d2 en lecture en boucle fd_set initial; int fd_max=0; FD_ZERO(&initial); FD_SET(d1,&initial); fd_max=maximum(fd_max,d1) ; //on suppose qu'il y a une fonction maximum FD_SET(d2,&initial); fd_max=maximum(fd_max,d2); while(1){ fd_set rdfs; //on initialisera a chaque tour cet ensemle FD_COPY(&initial,&rdfs); int res=select(fd_max+1, &rdfs, NULL, NULL, NULL) ; while(res 0){ if(fd_isset(d1,&rdfs)){... ; res-- ; if(fd_isset(d2,&rdfs)){... ; res-- ; PR - E/S NON BLOQUANT EN C 12 Que faut-il faire pour utiliser select Il faut se rappeler du descripteur maximal que l'on manipule Il faut créer les ensembles de file descriptor à donner en argument Comment manipuler les file descriptor : FD_SET(int fd, fd_set *set); Ajoute fd à l'ensemble FD_CLR(int fd, fd_set *set); Enlève fd de l'ensemble FD_ISSET(int fd, fd_set *set); Renvoie vraie si fd est dans l'ensemble. FD_ZERO(fd_set *set) ; Efface tous les éléments de l'ensemble FD_COPY(fd_set *orig, fd_set *copy); Copie orig dans copy Quand select termine, il renvoie le nombre de descripteurs sur lesquels les opérations ont été réalisées Les fd_set ont été modifiées!!!!!! Seuls les descripteurs 'disponibles' sont restées, on peut donc tester avec FD_ISSET qui est disponible Il faut réinitialiser les fd_set pour un prochain appel à select PR - E/S NON BLOQUANT EN C 13 À propos du timeout select n'est pas nécessairement complètement bloquant Comme on l'a vu on peut proposer un timeout dans le dernier argument Dans ce cas, soit select termine car il y a un événement soit parce que le temps d'attente à dépasser le timeout Le timeout est de type struct timeval *timeout struct timeval { int tv_sec; Exemple simple : // seconds int tv_usec; // microseconds ; Programme qui attend que l'on tape un caractère au clavier et si on ne le tape au bout de 2,5 seconds s'arrête PR - E/S NON BLOQUANT EN C 14 Exemple int main(void) { struct timeval tv; fd_set initial; int ret=0; tv.tv_sec = 2; tv.tv_usec = ; FD_ZERO(&initial); FD_SET(STDIN_FILENO, &initial); char mess[2]; while(1){ fd_set rfds; FD_COPY(&initial,&rfds); ret=select(stdin_fileno+1, &rfds, NULL, NULL, &tv); printf( valeur de retour de select : %d\n ,ret); if (FD_ISSET(STDIN_FILENO, &rfds)){ printf( on appuie sur une touche\n ); read(stdin_fileno,mess,1); mess[1]='\0'; printf( touche %s\n ,mess); else { printf( timed out.\n ); return 0; return 0; PR - E/S NON BLOQUANT EN C 15 Retour sur notre récepteur int main() { int sock1=socket(pf_inet,sock_dgram,0); struct sockaddr_in address_sock1; address_sock1.sin_family=af_inet; address_sock1.sin_port=htons(5555); address_sock1.sin_addr.s_addr=htonl(inaddr_any); int sock2=socket(pf_inet,sock_dgram,0); struct sockaddr_in address_sock2; address_sock2.sin_family=af_inet; address_sock2.sin_port=htons(5556); address_sock2.sin_addr.s_addr=htonl(inaddr_any); int r=bind(sock1,(struct sockaddr *)&address_sock1,sizeof(struct sockaddr_in)); if(r==0){ int r2=bind(sock2,(struct sockaddr *)&address_sock2,sizeof(struct sockaddr_in)); if(r2==0){ fcntl( sock1, F_SETFL, O_NONBLOCK); fcntl( sock2, F_SETFL, O_NONBLOCK); fd_set initial; int fd_max=0; FD_ZERO(&initial); FD_SET(sock1,&initial); if(fd_max sock1){fd_max=sock1; FD_SET(sock2,&initial); if(fd_max sock2){fd_max=sock2; PR - E/S NON BLOQUANT EN C 16 Retour sur notre récepteur char tampon[100]; int rec1=0; int rec2=0; while(1){ fd_set rdfs; FD_COPY(&initial,&rdfs); int ret=select(fd_max+1, &rdfs, NULL, NULL, NULL); while(ret 0){ if(fd_isset(sock1,&rdfs)){ rec1=recv(sock1,tampon,100,0); printf( taille de données recues %d\n ,rec1); if(rec1 =0){ tampon[rec1]='\0'; printf( message recu : %s\n ,tampon); ret--; if(fd_isset(sock2,&rdfs)){ rec2=recv(sock2,tampon,100,0); printf( taille de données recues %d\n ,rec2); if(rec2 =0){ tampon[rec2]='\0'; printf( message recu : %s\n ,tampon); ret--; return 0; PR - E/S NON BLOQUANT EN C 17 Autre méthode que select Il existe une autre fonction pour mettre en attente des processus sur différents descripteurs La fonction poll La structure est différente du select Alors que select manipuler un champ de bits (les fd_set) mis à 0 ou 1 selon si on voulait suivre on non le descripteur associé On fournit à la fonction poll un tableau de structure à suivre int poll(struct pollfd *ufds, unsigned int nfds, int timeout); ufds est un tableau de struct pollfd nfds est est la taille du tableau PR - E/S NON BLOQUANT EN C 18 Structure manipulée par poll struct pollfd { int fd; //le descripteur short events; / l'événement qui nous intéresse short revents; //l'événement quand poll retourne; Les évnénements possibles sont : POLLIN : lecture ou accept POLLOUT : écriture POLLPRI : lecture prioritaire POLLHUP : déconnexion POLLERR : erreur PR - E/S NON BLOQUANT EN C 19 Retour sur notre récepteur int main() { int sock1=socket(pf_inet,sock_dgram,0); struct sockaddr_in address_sock1; address_sock1.sin_family=af_inet; address_sock1.sin_port=htons(5555); address_sock1.sin_addr.s_addr=htonl(inaddr_any); int sock2=socket(pf_inet,sock_dgram,0); struct sockaddr_in address_sock2; address_sock2.sin_family=af_inet; address_sock2.sin_port=htons(5556); address_sock2.sin_addr.s_addr=htonl(inaddr_any); int r=bind(sock1,(struct sockaddr *)&address_sock1,sizeof(struct sockaddr_in)); if(r==0){ int r2=bind(sock2,(struct sockaddr *)&address_sock2,sizeof(struct sockaddr_in)); if(r2==0){ fcntl( sock1, F_SETFL, O_NONBLOCK); fcntl( sock2, F_SETFL, O_NONBLOCK); struct pollfd p[2]; p[0].fd=sock1; p[0].events=pollin; p[1].fd=sock2; p[1].events=pollin; PR - E/S NON BLOQUANT EN C 20 Retour sur notre récepteur char tampon[100]; int rec1=0; int i; while(1){ int ret=poll(p,2,-1); if(ret 0){ for(i=0;i 2;i++){ if(p[i].revents==pollin){ rec1=recv(p[i].fd,tampon,100,0); printf( taille de données recues %d\n ,rec1); if(rec1 =0){ tampon[rec1]='\0'; printf( message recu : %s\n ,tampon); return 0; PR - E/S NON BLOQUANT EN C 21
Related Search
Advertisements
Related Docs
View more...
We Need Your Support
Thank you for visiting our website and your interest in our free products and services. We are nonprofit website to share and download documents. To the running of this website, we need your help to support us.

Thanks to everyone for your continued support.

No, Thanks