[C] poll() & sockets

poll() & sockets [C] - C - Programmation

Marsh Posté le 20-03-2006 à 00:21:54    

Bonjour,
 
Je cherche à coder un serveur multi-clients et utiliser poll() pour la gestion des sockets.
 
J'ai fait quelque teste.
 

Code :
  1. if ((new_fd = accept(s_listen, (struct sockaddr *)&their_addr, (unsigned int *)&sin_size)) > 0) {
  2.   old_flags = fcntl(new_fd, F_GETFL, 0);  // On récupère l'ancien bitmask
  3.   fcntl(new_fd, F_SETFL, old_flags | O_NONBLOCK); // Et on lui ajoute le non bloquant = SOCKET non Bloquant
  4.   poll_list[0].fd = new_fd;
  5.   poll_list[0].events = POLLIN | POLLPRI;
  6.   while (1) {
  7.    poll(poll_list, 2, -1);
  8.    //printf("Entering...\n" );
  9.    if (poll_list[0].revents & (POLLIN)) {
  10.      printf("recu\n" );
  11.    }
  12.   }


 
Donc voilà, avec des sockets non bloquant.
 
Mais j'ai quelque problèmes :
- Y'a t'il un autre moyen qu'en whilant ou de verifier dans le while si il y'a eu un event au lieu de while(1).
- Pourquoi les flags de revents ne se mettent pas à jour à chaque boucle (si je recois une donnée, dans toutes les boucles suivante mon file descriptor aura le flag POLLIN.
 
Merci :)

Reply

Marsh Posté le 20-03-2006 à 00:21:54   

Reply

Marsh Posté le 20-03-2006 à 02:20:37    

Est ce que tu as pensé à utiliser des thread ou des processus?

Reply

Marsh Posté le 20-03-2006 à 02:44:22    

D habitude on utilise plutôt la fonction accept() sur un socket serveur bloquant. (voir man 2 accept, man 2 listen).
Puis quand une connection arrive, on crée une nouvelle thread ou un nouveau processu pour traiter la connexion.
 
poll() est plutôt utilisé sur plusieurs sockets à la fois. Là tu n en a qu une, et pourtant tu fait:
  poll(poll_list, 2, -1);
en est tu sur?
 
Dans ton code la ligne ...accept(listen(...))) n accepte qu une seule connexion client. Tu devrai mettre le listen avant et boucler sur accept. le poll() porte ici sur la socket client.

Reply

Marsh Posté le 20-03-2006 à 08:24:45    

J'ai juste montrer un exemple, mon serveur est censé accepter 2000 connection et mon listen se situe avant.
 
Je ne vais pas créer un thread pour 2000 connection :)

Reply

Marsh Posté le 20-03-2006 à 09:01:04    

Dinan a écrit :

J'ai juste montrer un exemple, mon serveur est censé accepter 2000 connection et mon listen se situe avant.
 
Je ne vais pas créer un thread pour 2000 connection :)


Evidemment non, mais il est courant de créer un thread par connexion. C'est fait pour. Il faut de la mémoire, c'est tout.


Message édité par Emmanuel Delahaye le 20-03-2006 à 09:01:53

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 20-03-2006 à 10:27:58    

et le retour de poll, tu t'en fiches ?

Reply

Marsh Posté le 20-03-2006 à 23:41:41    

Code :
  1. while (1) {
  2.    verif = poll(poll_list, 1, 5000);
  3.    if (verif == 0 || verif == -1) {
  4.     continue;
  5.    }
  6.    //printf("Entering...\n" );
  7.    if (poll_list[0].revents & (POLLIN)) {
  8.      printf("recu\n" );
  9.    }
  10.   }


 
Meme résultat :(((
 
Et pour les thread, je me suis renseigné apparement pour bcp de connection c'est trop lourd (mon serveur est sensez pouvoir recevoir 2000 connection mais par seconde, meme avec un pool thread serait trop lourd.
 
Mais bref ! j'ai toujour problème avec poll().
 
Quelqu'un aurait un tuto avec exemple mise à part le man ?
 
Merci :)

Reply

Marsh Posté le 20-03-2006 à 23:47:18    

si c'est -1, c'est sans doute une erreur, pas un cas à ignoré. voir perror()
 
c'est fait exprès les poll et le non-bloquant ? tu t'emmêles pas les pinceaux là ?  
 
et c'est quoi le problème en fait ? si ça reste sur POLLIN, c'est que y a des données, voilà tout, rien de sorcier ...

Reply

Marsh Posté le 21-03-2006 à 00:31:25    

Quitte à me répéter:
 
> Dans ton code la ligne ...accept(listen(...))) n accepte qu UNE seule connexion client.
 
> le poll() porte ici sur la socket CLIENT.
 

Code :
  1. // on boucle sur la socket client
  2.     while (1) {
  3. // on attends un évènement sur la socket client
  4.                     verif = poll(poll_list, 1, 5000);
  5.                     if (verif == 0 || verif == -1) {
  6. // on continue tranquillement même en cas d erreure
  7.                         continue;
  8.                     }
  9.                     //printf("Entering...\n" );
  10. // on a reçu un évènement sur la socket client
  11.                     if (poll_list[0].revents & (POLLIN)) {
  12. // l évènement est: donnée reçue
  13.                             printf("recu\n" );
  14. // on ne lit pas la donnée reçue, comme ça on peut continuer de boucler indéfiniment sur le même évènement
  15.                     }
  16.                  }


Reply

Marsh Posté le 21-03-2006 à 08:17:07    

Taz -> Non il n'ya aucune donnée.
nargy -> Je connect 1 seul socket en effet mais c'est pour faire mes teste, j'ai tester avec une 20 aine de connection c'est pareil

Reply

Marsh Posté le 21-03-2006 à 08:17:07   

Reply

Marsh Posté le 21-03-2006 à 08:21:20    

Je crois en faite que vous n'avez pas compris mon problème :
 
Pourquoi cela while a l'infinie sur "recu" dès que j'envoi 1 octect !

Reply

Marsh Posté le 21-03-2006 à 09:09:35    

tant que tu ne lis pas les données que tu as reçues, elles sont toujours disponibles et poll continue à te générer un événement POLLIN

Reply

Marsh Posté le 21-03-2006 à 16:56:51    

merci beaucoup franceso, je n'y avait pas pensé :)

Reply

Marsh Posté le 21-03-2006 à 22:15:09    

Pour savoir,
 
Normalement le flag POLLHUP ou POLLERR est sensé s'ajouter au bitmask quand le client se déco ?
 
Car moi pas du tout Oo
 
Merci

Reply

Marsh Posté le 21-03-2006 à 22:24:06    

et pourtant ...
 
ça peut varier en fonction de l'OS. si t'as un POLLIN de toutes façons, tu fais ton read, si ça te retourne 0, ben c'est EOF.


Message édité par Taz le 21-03-2006 à 22:25:44
Reply

Marsh Posté le 21-03-2006 à 22:31:24    

Oui je sait bien pour le read mais j'aurait autant aimer exploiter a fond poll.
Pour info je suis sous Debian.
 
Enfin merci ^^

Reply

Marsh Posté le 21-03-2006 à 23:30:46    

Code :
  1. maxfd++;
  2.   old_flags = fcntl(new_fd, F_GETFL, 0);  // On récupère l'ancien bitmask
  3.   fcntl(new_fd, F_SETFL, old_flags | O_NONBLOCK); // Et on lui ajoute le non bloquant = SOCKET non Bloquant
  4.   poll_list[maxfd].fd = new_fd;
  5.   poll_list[maxfd].events = POLLIN | POLLPRI;
  6.   while (1) {
  7.    verif = poll(poll_list, maxfd, -1);
  8.    if (verif < 0) {
  9.     continue;
  10.    }
  11.    for (i = 1; i <= maxfd; i++) {
  12.     if (poll_list[i].fd == -1) {
  13.      continue;
  14.     }
  15.     //printf("Entering...\n" );
  16.     if (poll_list[i].revents & (POLLERR | POLLHUP)) {
  17.      printf("ended\n" );
  18.      break;
  19.     }
  20.     if (poll_list[i].revents & (POLLIN)) {
  21.       printf("recu %i\n", i);
  22.       if (recv(poll_list[i].fd, pSock, 512, 0) < 1) {
  23.        poll_list[i].fd = -1;
  24.       }
  25.     }
  26.    }
  27.   }


 
Etant donné que mon sock est non bloquant, comment eviter la charge maximum du CPU ?
 
Merci :)

Reply

Marsh Posté le 22-03-2006 à 00:05:31    

Reply

Marsh Posté le 22-03-2006 à 11:27:54    

#for (i = 1; i <= maxfd; i++)
 
tu zappe le premier élément et overflow. on fait pas du pascal.
 
# poll(poll_list, maxfd, -1);
avec ça meme avec overflow, tu peux être sur que ton new_fd ne sera jamais traité
 
#if (poll_list[i].fd == -1)
 
je pense qu'avec ça, poll va te renvoyer -1
 
#if (poll_list[i].revents & (POLLERR | POLLHUP) {
# printf("ended\n" );
tu n'écoutes rien. je t'ai dit que s'il y a EOF, tu auras POLLIN
 
#if (recv(poll_list[i].fd, pSock, 512, 0) < 1)  
-1 ou 0 ça fait une sacrée différence.
 
# poll_list[i].fd = -1;
vire plutot ce descripteur de la liste

Reply

Marsh Posté le 22-03-2006 à 11:28:32    

Dinan a écrit :

Code :
  1. verif = poll(poll_list, maxfd, -1);


 
Etant donné que mon sock est non bloquant, comment eviter la charge maximum du CPU ?
 
Merci :)


lire le man de poll


Message édité par Taz le 22-03-2006 à 11:29:20
Reply

Marsh Posté le 22-03-2006 à 11:30:15    


jamais. c'est stupide, c'est juste la meilleure façon de s'assurer que son application va tourner le plus lentement possible.

Reply

Marsh Posté le 23-03-2006 à 08:18:06    

Taz ma question n'était que sur la charge du CPU, pas sur le reste (le reste n'est pas un code que je vait utiliser, je met des bouts de code pour tester différentes solutions.) enfin merci quand meme et nargy j'ai jamais vu qu'on ralentissait une application pour moins de charge.

Reply

Marsh Posté le 23-03-2006 à 10:17:15    

usleep(0)
Ça fait pas aller le processus le plus lentement possible, ça oblige l OS à changer changer de contexte quand tu n a plus rien à faire dans ta boucle while(1), et c est tout.
S il ne revient pas de usleep(0), c est qu il y a un autre processus fou qui fait un while(1).

Reply

Marsh Posté le 23-03-2006 à 10:21:29    

> jamais. c'est stupide, c'est juste la meilleure façon de s'assurer que son application va tourner le plus lentement possible.
 
Quand tu n a rien à faire dans un while(1), la seule chose qui te reste à faire c est tourner le plus lentement possible.
C est pas parce que tu bouffe plus de CPU que les connexions vont arriver!

Reply

Marsh Posté le 23-03-2006 à 11:04:39    

Citation :

usleep(0)
Ça fait pas aller le processus le plus lentement possible, ça oblige l OS à changer changer de contexte quand tu n a plus rien à faire dans ta boucle while(1), et c est tout.  
 
C est pas parce que tu bouffe plus de CPU que les connexions vont arriver!


certes, mais si tu fais un usleep(t), tu traiteras en moyenne tes connexions entrantes avec un retard de t/2.
Si tu fais un usleep(0), tu vas effectivement laisser la main aux autres processus, mais tu vas quand même bouffer toute la puissance restante du processeur.
 
poll() et select() sont justement faits pour éviter ce genre de désagréments et placer ton processus en attente jusqu'au moment où il a quelque chose à faire.

Reply

Marsh Posté le 23-03-2006 à 13:04:10    

franceso> entièrement d accord
 
J ai récupéré le nombre maximum de processus que préconise le noyau linux 2.4: 4096 (getrlimit)
 
On est donc en dessous avec 2000. Le traitement par thread ou processus reste envisageable.
 
Au delà de cette valeur, il est préférable d avoir un load balancing.
 
Le choix final dépends de la taille des connexions traitées, ``4096`` correspondant sûrement à l ordre de grandeur du kilooctet.
 
Pour des mégaoctets ou des octets il faudrait des tests. C est à Dinan de voir.

Reply

Marsh Posté le 23-03-2006 à 18:02:50    

Non les thread pour mon projet n'est pas une solution étant donné que les connections par clients seront toute les 2sec environs. De toute facon je n'est aucunement donné un avis sur la facon dont je traiterait mes clients.
Poll reste la meilleur solution d'après nombre d'avis.
 
Enfin, merci, je vait chercher pour la charge de Poll.
 
Merci fransesco

Reply

Marsh Posté le 24-03-2006 à 10:09:17    

Citation :

Non les thread pour mon projet n'est pas une solution étant donné que les connections par clients seront toute les 2sec environs.

c'est pas seulement ça qui est important ; c'est aussi le temps que tu mets pour servir un client. Si tes connexions arrivent toutes les deux secondes, mais que tu mets 10 minutes à traiter la demande d'un client, tu vas pas t'en sortir avec un seul thread/processus.
 

Citation :

Merci fransesco

De rien, mais c'est franceso

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed