IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Terminer proprement une boucle d'écoute


Sujet :

C

  1. #1
    Membre régulier
    Inscrit en
    Décembre 2003
    Messages
    99
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 99
    Points : 82
    Points
    82
    Par défaut Terminer proprement une boucle d'écoute
    Bonjour,

    j'ai une boucle d'écoute de ce type dans le main :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    for (;;) {
      addrlen = sizeof(socketaddr);
      int workersock = accept(listensock, (struct sockaddr *)&socketaddr, socklen_t *)&addrlen);
      printf("ICI1\n");
      if (workersock < 0) {
           printf("ICI2\n");
           break;
      }
      [...]
    }
    printf("FIN\n");
    Quand je lance le programme , il se bloque bien sur l'accept, mais à chaque fois que je termine le programme, il ne passe jamais dans les printf et encore moins dans le dernier printf.

    Je ne comprend pas pourquoi, c'est surement très bête mais je vois pas, avez-vous une idée ?

    J'ai besoin de sortir de la boucle d'écoute lors de la terminaison du programme, pour nettoyer les structures, terminer des threads et fermer des sockets.

    Merci d'avance pour votre aide.
    "Il n'existe que deux choses infinies, l'univers et la bêtise humaine... mais pour l'univers, je n'ai pas de certitude absolue." A. Einstein

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Citation Envoyé par chicorico Voir le message
    Quand je lance le programme , il se bloque bien sur l'accept, mais à chaque fois que je termine le programme, il ne passe jamais dans les printf et encore moins dans le dernier printf.
    Tu termines ton programme comment ? Si c'est avec un break en faisant un Ctrl-C, c'est normal qu'il n'aille pas au bout.

    J'ai besoin de sortir de la boucle d'écoute lors de la terminaison du programme, pour nettoyer les structures, terminer des threads et fermer des sockets. Merci d'avance pour votre aide.
    Je fais l'hypothèse que tu travailles sous Unix. Il y a deux points à régler pour faire cela :

    1. La possibilité de quitter l'attente s'il se passe quelque chose à côté, notamment sur l'entrée standard. Vois du côté de select(). Fais une recherche sur ce forum, le sujet a été traité en profondeur à de maintes reprises ;
    2. Faire en sorte que le programme sorte quand même proprement si c'est l'utilisateur qui fait un Ctrl-C. Pour cela, il te faut intercepter et traiter les bons « signaux ». Fais man signal, man 7 signal et man sigaction.

  3. #3
    Membre régulier
    Inscrit en
    Décembre 2003
    Messages
    99
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 99
    Points : 82
    Points
    82
    Par défaut
    Bonjour,

    Merci pour ta réponse, effectivement je travaille sous linux.

    Tu termines ton programme comment ? Si c'est avec un break en faisant un Ctrl-C, c'est normal qu'il n'aille pas au bout.
    En fait avec un Ctrl-C, pourquoi ne vas-t-il pas jusqu'au bout ?

    Pour le select, je n'ai malheureusement pas le temps de redévelopper tout le serveur:-).

    J'aurais aimé éviter l'utilisation des signaux et terminer de façon standard. Mais si il y n'y pas d'autre solution.
    J'ai essayer de capturer les signaux, mais il y a encore des cas ou ça ne semble pas passer. J'en oublie peut-être , voici les signaux que j'utilise : SIGTERM,SIGINT,SIGQUIT,SIGALRM,SIGUSR1,SIGUSR2

    Je positionne le sa_flags (de la structure sigaction) à SA_RESTART , mais je ne suis pas sur d'avoir bien compris à quoi sert ce flags ?
    "Il n'existe que deux choses infinies, l'univers et la bêtise humaine... mais pour l'univers, je n'ai pas de certitude absolue." A. Einstein

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Citation Envoyé par chicorico Voir le message
    En fait avec un Ctrl-C, pourquoi ne vas-t-il pas jusqu'au bout ?
    Parce que c'est l'objet de cette commande : taper Ctrl-C dans un terminal envoie SIGINT au processus de premier plan (et, paraît-il, à tous les processus du terminal sous Solaris). Et SIGINT est un ordre d'arrêt immédiat. Ça a été repris sur énormément de plateformes. Dans les BASIC des années 1980, par exemple, Ctrl-C servait à faire un break, donc à interrompre l'exécution du programme en cours.

    Imagine que tu sois bloqué dans une boucle infinie. Comment ferais-tu pour stopper ton programme si Ctrl-C était censé attendre que le programme se finisse tranquillement ?

    Pour le select, je n'ai malheureusement pas le temps de redévelopper tout le serveur:-).
    Tu n'as pas besoin de re-développer le serveur entier pour utiliser select(). Mais tu m'inquiètes du coup : comment fais-tu pour gérer tes différentes connexions ? Ne me dis pas que tu as d'emblée utilisé un thread par connexion, quand même ?

    J'aurais aimé éviter l'utilisation des signaux et terminer de façon standard. Mais si il y n'y pas d'autre solution. J'ai essayer de capturer les signaux, mais il y a encore des cas ou ça ne semble pas passer. J'en oublie peut-être , voici les signaux que j'utilise : SIGTERM,SIGINT,SIGQUIT,SIGALRM,SIGUSR1,SIGUSR2
    Il ne s'agit pas d'utiliser en soi les signaux pour faire ce que tu veux faire mais de prévoir leur arrivée, car il te sont envoyés par une entité extérieur à ton programme (en substance le système, éventuellement à l'initiative de l'utilisateur).

    L'idée est que tu reçois un signal du système qui est impératif car soit il concerne le système entier (exemple : « attention, la machine s'arrête. Si vous avez des trucs à sauver, faites-le maintenant »), soit parce que ton programme ne peut pas continuer en l'état car c'est son contexte qui n'est plus valable (exemple : « tu n'as plus de sortie standard car l'utilisateur a refermé le terminal dans lequel tu t'exécutes »).

    De là, trois possibilités :
    • soit ça n'a aucune importance et le signal est ignoré par défaut ;
    • soit le cas de figure doit être traité mais, de toute évidence, le programme n'est pas prévu pour. Dans ce cas, le système met fin à ton programme, par défaut ;
    • soit ton programme a prévu le cas et a remplacé le handler par défaut du signal par le sien.


    Il faut savoir aussi que la réception d'un signal, si elle ne provoque pas immédiatement la mort d'un processus, provoque toujours son déblocage s'il était dans cet état. C'est vrai avec accept() ou read() mais aussi avec sleep() ou tout autre fonction potentiellement bloquante. Donc, en l'occurrence, ignorer SIGINT pourrait de fait faire faire à ton programme ce que tu veux qu'il fasse, mais ce ne serait que par accident.

    Le mieux reste select().

    Je positionne le sa_flags (de la structure sigaction) à SA_RESTART , mais je ne suis pas sur d'avoir bien compris à quoi sert ce flags ?
    Dans ce cas, soit il ne faut pas l'utiliser, soit il faut lire la doc :

    Citation Envoyé par man 7 sigaction
    SA_RESTART
    Fournir un comportement compatible avec la sémantique BSD en redémarrant automatiquement les appels système lents interrompus par l'arrivée du signal. Cet attribut n'a de sens que lors de la mise en place d'un gestionnaire de signal. Voir signal(7) pour une discussion sur le redémarrage d'un appel système.

    J'entends par là que si tu évites d'utiliser ce que tu ne comprends pas, tu laisses ton programme suivre son déroulement normal et, donc, tu vois comment les événements se déroulent. En l'occurrence, ce flag sert à contrecarrer l'effet que je te cites plus haut et à redémarrer automatiquement l'appel système interrompu plutôt que passer à la suite.

  5. #5
    Membre régulier
    Inscrit en
    Décembre 2003
    Messages
    99
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 99
    Points : 82
    Points
    82
    Par défaut
    Bonjour,

    Merci pour toutes ces réponses complètes.

    Imagine que tu sois bloqué dans une boucle infinie. Comment ferais-tu pour stopper ton programme si Ctrl-C était censé attendre que le programme se finisse tranquillement ?
    Effectivement ça serait embêtant :-), mais je pensais que accept se débloquait lors de la réception d'un signal SIGINT, et retournait un descripteur < 0, et il reste toujours le SIGKILL pour débloquer une boucle infinie ;-).

    Mais tu m'inquiètes du coup : comment fais-tu pour gérer tes différentes connexions ? Ne me dis pas que tu as d'emblée utilisé un thread par connexion, quand même ?
    Euh, si :-). Le problème n'est pas de gérer plusieurs connexion avec les threads, mais que le traitement réalisé dans une connexion prenne un certains temps et donc bloque le traitement d'une prochaine connexion tant que le traitement n'est pas terminé. Par conséquent, utiliser select ou accept revient au même dans mon cas, il faut mettre le traitement dans un thread pour ne pas bloquer le traitement des connections suivantes.

    J'ai initié un gestionnaire de signaux(avec un handler) et il a l'air de marcher plutôt bien :-). Je vais rester sur cette méthode.

    Pour le SA_RESTART, j'avais lu la doc , mais je ne la trouve pas clair.Notamment la partie "appels système lent", aurais-tu un exemple d'utilisation ?

    Merci encore !
    "Il n'existe que deux choses infinies, l'univers et la bêtise humaine... mais pour l'univers, je n'ai pas de certitude absolue." A. Einstein

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Hello,

    Citation Envoyé par chicorico Voir le message
    Effectivement ça serait embêtant :-), mais je pensais que accept se débloquait lors de la réception d'un signal SIGINT, et retournait un descripteur < 0, et il reste toujours le SIGKILL pour débloquer une boucle infinie ;-).
    C'est-à-dire que SIGKILL, il ne faut l'utiliser qu'en dernier ressort, si vraiment ton programme ne répond pas du tout (notamment si le handler de signal lui-même est pourri). Le SIGKILL ne peut pas être capturé par un handler et fait exactement ce que tu cherches à éviter : tuer le programme sans passer par la procédure de sortie « propre ».

    Euh, si :-). Le problème n'est pas de gérer plusieurs connexion avec les threads, mais que le traitement réalisé dans une connexion prenne un certains temps et donc bloque le traitement d'une prochaine connexion tant que le traitement n'est pas terminé. Par conséquent, utiliser select ou accept revient au même dans mon cas, il faut mettre le traitement dans un thread pour ne pas bloquer le traitement des connections suivantes.
    Ok. Admettons. Mais si lancer un thread par connexion simplifie pas mal de choses, c'est un peu la solution de facilité et elle pose, à terme, pas mal de problèmes, à commencer par la montée en charge.

    J'ai initié un gestionnaire de signaux(avec un handler) et il a l'air de marcher plutôt bien :-). Je vais rester sur cette méthode.
    En l'occurrence, ça marche bien parce que tu tombes au bon endroit, mais la méthode propre consiste quand même à prévoir une porte de sortie « officielle » à ton programme. D'où le select() : ça te permettrait non seulement de gérer plusieurs sockets à la fois, mais aussi et surtout de surveiller le clavier en plus de tes sockets.

    Pour le SA_RESTART, j'avais lu la doc , mais je ne la trouve pas clair.Notamment la partie "appels système lent", aurais-tu un exemple d'utilisation ?
    C'est-à-dire qu'en réalité, la réception d'un signal interrompt tous les appels systèmes quand c'est possible. Un appel système rend en général la main immédiatement, sauf quand ils sont bloquants parce qu'en attente d'un événement particulier, ou de données extérieures. Je pense que c'est cela que la man page entend par « appel lent ».

    Merci encore !
    À ton service.

Discussions similaires

  1. Terminer proprement une boucle infinie
    Par pynoob dans le forum Général Python
    Réponses: 11
    Dernier message: 10/02/2011, 19h23
  2. Réponses: 5
    Dernier message: 03/02/2010, 23h18
  3. Terminer proprement une vidéo
    Par attwad dans le forum OpenCV
    Réponses: 1
    Dernier message: 31/10/2008, 07h51
  4. terminer une boucle Do Loop
    Par svedberg dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 15/10/2007, 03h17
  5. evenement pour terminer une boucle
    Par ZaaN dans le forum MFC
    Réponses: 1
    Dernier message: 06/12/2005, 10h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo