Arreter un serveur multiclient

Voir le sujet précédent Voir le sujet suivant Aller en bas

Arreter un serveur multiclient

Message  chouchou le Lun 3 Nov 2008 - 18:09

Bonjour à tous,
Ça a déjà été dit mais un grand merci à l'auteur de ce site riche en informations précieuses et claires.

J'ai quelques questions à propos de l'exemple de serveur TCP/IP(le tout dernier 08s.c) donné dans l'article http://mapage.noos.fr/emdel/reseaux.htm#projet_reseau.
Dans cet exemple la fonction clients attend la connexion d'un client puis lance le thread client et boucle indéfiniment.
Supposons que la fonction clients(celle qui lance les threads) puisse s'arrêter sur un évènement extérieur.
Peut on (et si oui faut-il):
- "dire" a chaque thread qu'il faut qu'il s'arrête
- et donc fermer toutes les sockets associées
?
J'ai vu qu'il existe des fonctions (pthread_cancel...) qui permettent d'arrêter un thread depuis un autre thread.
Mais pour cet exemple comment récupérer simplement l'identifiant de tous les threads?
Et puis si le thread s'arrête brutalement on ne pourra pas prévenir les clients de la déconnexion?

Merci

chouchou

Messages : 6
Date d'inscription : 03/11/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  -ed- le Mar 4 Nov 2008 - 1:45

chouchou a écrit:J'ai quelques questions à propos de l'exemple de serveur TCP/IP(le tout dernier 08s.c) donné dans l'article http://mapage.noos.fr/emdel/reseaux.htm#projet_reseau.
Dans cet exemple la fonction clients attend la connexion d'un client puis lance le thread client et boucle indéfiniment.
Supposons que la fonction clients(celle qui lance les threads) puisse s'arrêter sur un évènement extérieur.
Peut on (et si oui faut-il):
- "dire" a chaque thread qu'il faut qu'il s'arrête
- et donc fermer toutes les sockets associées
?
J'ai vu qu'il existe des fonctions (pthread_cancel...) qui permettent d'arrêter un thread depuis un autre thread.
Mais pour cet exemple comment récupérer simplement l'identifiant de tous les threads?
Et puis si le thread s'arrête brutalement on ne pourra pas prévenir les clients de la déconnexion?
Dans les exemples que j'ai donné, j'ai surtout paré au plus pressé et je n'ai pas approfondi.

Il y a plusieurs problèmes à considérer. D'une manière générale, dans un serveur, il y a

- la boucle accept()
- les boucles recv() des clients.

J'utilise exprès ces deux fonctions pour 'nommer' les boucles, car ces fonctions ont des propriétés remarquables :

1 - Elles sont bloquantes, ce qui constitue d'excellents candidats à la mise en threads (et on ne s'en prive pas).
2 - Elles dépendent de l'activité d'un socket. C'est très important, car ces fonctions ne sont bloquantes que si le socket est actif. En cas de déconnexion locale (closesocket()) ou distante, ces fonctions ne sont plus bloquantes et elle retournent 0. On voit tout de suite que ça va pouvoir permettre de détecter la fin de boucle et donc la fermeture automatique du thread.

Le thread client

Le thread client de base "naïf" traitant du texte ressemble à ça :
Code:

void *tache_cli (void *user)
{
  if (user != NULL)
  {
      struct cli *p = user;

      while (1)
      {
        char data[SIZE];
        int n = recv (p->sock, data, sizeof data- 1, 0);
        if (n > 0)
        {
            data[n] = 0;
            process_cli(p, data);
        }
      }
  }
  return NULL;
}
Pour terminer ce thread proprement, la seul solution est de sortir de la boucle. Quelles sont les conditions qui pourraient faire sortir de la boucle ?

- une déconnexion du client (distante) : recv() retourne 0
- une déconnexion locale (closesocket() sur reconnaissance d'une commande distante particulière ("quit" "bye" "disc" etc.) ou sur une commande issue de la console de gestion du serveur : recv() retourne aussi 0.

Et en fait, c'est tout. Les méthodes barbares de suppressions de thread sont dangereuses et ne mènent à rien. Il ne faut pas perdre de vue qu'une déconnexion est une partie bien définie du protocole utilisé. On ne résout rien en retirant la prise !

Ca donnerait tout simplement :
Code:

void *tache_cli (void *user)
{
  if (user != NULL)
  {
      struct cli *p = user;
      int end = 0;
      while (!end)
      {
        char data[SIZE];
        int n = recv (p->sock, data, sizeof data- 1, 0);
        if (n > 0)
        {
            data[n] = 0;
            process_cli(p, data);
        }
        else
        {
            err = 1;
        }
      }

      if (p->sock != INVALID_SOCKET)
      {
        closesocket (p->sock), p->sock = INVALID_SOCKET;
      }
  }
  return NULL;
}

Le thread serveur

Pour la boucle accept(), c'est pareil avec le socket serveur.


Dernière édition par -ed- le Mar 4 Nov 2008 - 12:51, édité 1 fois

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  chouchou le Mar 4 Nov 2008 - 12:21

Si je comprend bien, l'exemple que vous donnez, c'est le cas où le client interrompt la connexion en fermant sa socket par exemple?
Par contre je ne comprend pas bien comment ça peut se passer sur la boucle accept() du coté du serveur.
Je pense qu'il me manque:
ed a écrit:[...]conditions qui pourraient faire sortir de la boucle: [...]une déconnexion locale [...] sur une commande issue de la console de gestion du serveur
(je ne vois pas trop comment on peut intercepter ça ...)

J'explique le scénario que j'imagine:
Je reprends la fonction clients donnée dans l'article

Code:

static int clients (SOCKET sock)
{
  int err = 0;
  int end = 0;
  do
  {
      /* wait for a client connection */
      printf ("waiting for a client connection on port %d...\n", PORT);

      {
        /* create a new client context */
        struct cli *p_cli = malloc (sizeof *p_cli);
        if (p_cli != NULL)
        {
            p_cli->recsize = (int) sizeof p_cli->sin;
            p_cli->sock =
              accept (sock, (SOCKADDR *) &p_cli->sin, &p_cli->recsize);

            if (p_cli->sock != INVALID_SOCKET)
            {
              printf
                  ("client connected with socket %d from %s:%d\n",
                  p_cli->sock, inet_ntoa (p_cli->sin.sin_addr),
                  htons (p_cli->sin.sin_port));

              /* send ...*/
              pthread_create (&p_cli->thread, NULL, client, p_cli);
              /* ... and forget */
              p_cli = NULL;

            }
            else
            {
              perror ("socket.accept");
              err = 1;
            }
        }
        else
        {
            fprintf (stderr, "client creation failed : memory error\n");
        }
      }
  }
  while (!end);
  return err;
}

3 clients (par exemple) se connectent, on revient donc à la fonction bloquante accept().

L'utilisateur ayant accès au serveur ferme "directement" le serveur (en envoyant une commande QUITTER récupérée par un autre thread sur le serveur qui surveille les commandes utilisateur) sans vérifier qu'aucun client n'est déjà lancé.

- Comment récupérer cet évènement?

- Comment faire passer le end de la boucle à 1?

- Comment se déconnecter des 3 clients?
Je pense qu'il faut, à partir de la fonction clients() fermer les sockets associées (encore faut il les avoir conserver dans une liste par exemple)?

- et donc arrêter les threads associés?

- Avant de finalement arrêter la fonction clients()...

chouchou

Messages : 6
Date d'inscription : 03/11/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  -ed- le Mar 4 Nov 2008 - 13:15

chouchou a écrit:Si je comprend bien, l'exemple que vous donnez, c'est le cas où le client interrompt la connexion en fermant sa socket par exemple?
Oui, c'est à ma connaissance la seule façon de fermer proprement une connexion.

Par contre je ne comprend pas bien comment ça peut se passer sur la boucle accept() du coté du serveur.
Je pense qu'il me manque:
ed a écrit:[...]conditions qui pourraient faire sortir de la boucle: [...]une déconnexion locale [...] sur une commande issue de la console de gestion du serveur
(je ne vois pas trop comment on peut intercepter ça ...)
Il faut un thread client (ou console) dédié à l'administration du serveur et qui est capable de comprendre une commande de fin ("quit", "exit" etc.). Celle-ci a pour effet de déconnecter tous les clients proprement, entrainant la mort des threads clients comme indiqué au-dessus, puis de fermer la socket serveur, ce qui va rendre le accept() non-bloquant (il retourne alors 0), ce qui a pour effet de quitter la boucle accept(). On oublie pas non plus qui quitter la boucle de l'interpréteur de commandes d'administration et on fini par quitter le serveur proprement, en ayant fermé toutes les connexions.


J'explique le scénario que j'imagine:
Je reprends la fonction clients donnée dans l'article
Déjà, il faudrait faire évoluer ce code de façon à ce qu'il tienne compte de la fermeture du cskcket serveur :


Code:

static int clients (SOCKET sock)
{
  int err = 0;
  int end = 0;
  do
  {
      /* wait for a client connection */
      printf ("waiting for a client connection on port %d...\n", PORT);

      {
        /* create a new client context */
        struct cli *p_cli = malloc (sizeof *p_cli);
        if (p_cli != NULL)
        {
            p_cli->recsize = (int) sizeof p_cli->sin;
            p_cli->sock =
              accept (sock, (SOCKADDR *) &p_cli->sin, &p_cli->recsize);

            switch (p_cli->sock)
            {
            case 0:
              end = 1;
              break;
            case INVALID_SOCKET:
              err = 1;
              end = 1;
              break;
            default:
              printf
                  ("client connected with socket %d from %s:%d\n",
                  p_cli->sock, inet_ntoa (p_cli->sin.sin_addr),
                  htons (p_cli->sin.sin_port));

              /* send ...*/
              pthread_create (&p_cli->thread, NULL, client, p_cli);
              /* ... and forget */
              p_cli = NULL;
            }
        }
        else
        {
            fprintf (stderr, "client creation failed : memory error\n");
        }

        if (end && p_cli != NULL)
        {
            free (p_cli), p_cli = NULL;
        }
      }
  }
  while (!end);
  return err;
}
NON TESTE.

3 clients (par exemple) se connectent, on revient donc à la fonction bloquante accept().

L'utilisateur ayant accès au serveur ferme "directement" le serveur (en envoyant une commande QUITTER récupérée par un autre thread sur le serveur qui surveille les commandes utilisateur) sans vérifier qu'aucun client n'est déjà lancé.
C'est un peu subtil que ça, comme expliqué au-dessus...

- Comment récupérer cet évènement?
Il n'y a rien à récupérer. Il suffit de fermer le socket serveur. (closesocket).

- Comment faire passer le end de la boucle à 1?
C'est la fermeture du socket serveur qui débloque accept(), entrainant un retour 0, la mise à un de end et la fin de la boucle...

- Comment se déconnecter des 3 clients?
Je pense qu'il faut, à partir de la fonction clients() fermer les sockets associées (encore faut il les avoir conserver dans une liste par exemple)?
Oui, il faut garder une liste des sockets clients pour pouvoir les fermer.

- et donc arrêter les threads associés?

- Avant de finalement arrêter la fonction clients()...
Tout à fait. Il y a pas mal de modifications à faire.

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  chouchou le Mar 4 Nov 2008 - 14:25

Oky merci beaucoup, je crois que je commence à comprendre...
Je n'avais pas du tout pensé à un client pour l'administration!

  • Pour utiliser ce client servant à administrer(disons client_admin)
    - Doit on créer un socket serveur différent de celui utilisé pour les autres clients "classiques" ?(y'a -t-il un intérêt à changer le port d'écoute?)

  • Si j'ai bien compris ,au démarrage du serveur:
    - on crée un socket sock_admin
    - on fait un appel à listen(sock_admin,1)
    - puis un appel à accept()
    - une fois que client_admin est connecté on lance le thread tache_cli_admin()
    - ensuite on crée un socket sock_classique
    - et là on fait appel à la fonction clients(sock_classique) vue plus haut...

  • A quel moment on démarre client_admin()?
    - Si on le fait avant que le serveur fasse le listen(sock_admin,1) il risque d'y avoir une erreur au moment de l'appel à connect() dans client_admin?
    - Et si le serveur fait un listen() il va bloquer dessus tant qu'il n'y a pas de client....

  • C'est bien tache_cli_admin qui doit fermer les sockets des clients classiques? et fermer ensuite la socket serveur?

chouchou

Messages : 6
Date d'inscription : 03/11/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  -ed- le Mar 4 Nov 2008 - 14:38

chouchou a écrit:Oky merci beaucoup, je crois que je commence à comprendre...
Je n'avais pas du tout pensé à un client pour l'administration!

[*]Pour utiliser ce client servant à administrer(disons client_admin)
- Doit on créer un socket serveur différent de celui utilisé pour les autres clients "classiques" ?(y'a -t-il un intérêt à changer le port d'écoute?)
Pas forcément. Ca peut être un client comme un autre, simplement, il a des droits supplémentaires. A toi de gérer tes utilisateurs et tes mots de passe comme il faut.


[*]Si j'ai bien compris ,au démarrage du serveur:
- on crée un socket sock_admin
- on fait un appel à listen(sock_admin,1)
- puis un appel à accept()
- une fois que client_admin est connecté on lance le thread tache_cli_admin()
- ensuite on crée un socket sock_classique
- et là on fait appel à la fonction clients(sock_classique) vue plus haut...
On peut faire ça, oui. Mais comme dit plus haut, ce n'est pas obligatoire.

[*]A quel moment on démarre client_admin()?
- Si on le fait avant que le serveur fasse le listen(sock_admin,1) il risque d'y avoir une erreur au moment de l'appel à connect() dans client_admin?
- Et si le serveur fait un listen() il va bloquer dessus tant qu'il n'y a pas de client....
Dès que le accept() se débloque. Tout ce qui est bloquant doit être mis dans un thread...

[*]C'est bien tache_cli_admin qui doit fermer les sockets des clients classiques? et fermer ensuite la socket serveur?
Oui. Mais je crois que tu te compliques la tâche. Je suis personnellement pour la banalisation des clients et une bonne gestion des droits. On sépare ainsi l'aspect communication de l'aspect application...

Mais il y a certainement plusieurs solutions...

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  chouchou le Mar 4 Nov 2008 - 17:19

Ah oui effectivement un client normal avec une gestion des droits ça ma l'air plus simple.:)

Il reste une petite chose que je n'ai pas comprise:
Le client qui va servir à l'administration doit pouvoir se connecter au serveur à n'importe quel moment.

Vu que le nombre de connexions est limité par listen() je pense qu'il faut démarrer ce client par défaut au démarrage du serveur (on aura en fait une "super appli" qui lance à la fois le serveur et le client admin).
Il faut donc lancer 2 threads (un pour le serveur un pour le client).

Dans le client je ne peux lancer la commande connect() qu'à partir du moment où le serveur à fait appel à listen()?

Si c'est bien le cas comment savoir à quel moment le serveur à fait cet appel à listen()? Un simple flag juste avant l'appel à listen() suffit-il?

chouchou

Messages : 6
Date d'inscription : 03/11/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  -ed- le Mar 4 Nov 2008 - 17:51

chouchou a écrit:Il reste une petite chose que je n'ai pas comprise:
Le client qui va servir à l'administration doit pouvoir se connecter au serveur à n'importe quel moment.

Vu que le nombre de connexions est limité par listen()
Pas du tout. Comme souvent en informatique, le sens des mots n'est pas le sens apparent. listen() a un comportement bien défini qui n'a rien à voir avec ce que tu dis. Je t'invite à relire attentivement la doc de listen().

Ceci dit, le nombre de clients peut être effectivement limité par des problèmes de taille mémoire ou de nombre de threads pouvant être gérés simultanément par un processus. Il suffit alors d'en réserver un d'avance. Si le client qui se connecte au dernier thread possible est un client ordinaire, il est immédiatement déconnecté, et la place est laissée libre pour le client administrateur. Si dans un cas très rare, le client administrateur n'a pu se connecter une première fois, il suffit qu'il retente sa chance. Il est peu probable qu'il retombe sur le coyurt instant où tous les threads sont pris...

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  chouchou le Mar 4 Nov 2008 - 18:45

-ed- a écrit:Comme souvent en informatique, le sens des mots n'est pas le sens apparent. listen() a un comportement bien défini qui n'a rien à voir avec ce que tu dis. Je t'invite à relire attentivement la doc de listen().
Je me suis peut être mal exprimé(ou j'ai peut être effectivement rien compris)
listen() ne limite pas le nombre de clients connectés.
Mais j'ai lu que ça limitait le nombre de connexions entrantes.
Mon interprétation de connexions entrantes: connexions qui n'ont pas encore été traitées et qui "s'entassent" dans un file d'attente qui elle est limitée. Mais peut être que cette file (sachant que chaque connexion va être traitée "immédiatement" par un thread) ne sera jamais saturée...

Pour les threads, comment sait-on qu'on en est au dernier thread possible? On doit gérer une variable compteur? Ou il existe une fonction propre au threads?

chouchou

Messages : 6
Date d'inscription : 03/11/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  -ed- le Mar 4 Nov 2008 - 19:11

chouchou a écrit:
-ed- a écrit:Comme souvent en informatique, le sens des mots n'est pas le sens apparent. listen() a un comportement bien défini qui n'a rien à voir avec ce que tu dis. Je t'invite à relire attentivement la doc de listen().
Je me suis peut être mal exprimé(ou j'ai peut être effectivement rien compris)
listen() ne limite pas le nombre de clients connectés.
Mais j'ai lu que ça limitait le nombre de connexions entrantes.
Non. Ca indique combien de demandes de connexion simultanées peuvent être traitées en même temps. La boucle accept() met un certain temps à faire le tour. Si pendant ce temps, une demande de connexion arrive, elle n'est pas perdue. Si on a réglé listen() à 5, 5 demandes peuvent avoir lieu et la 6 ème est perdue (le client reçoit 'connexion impossible', mais il peu retenter quelques secondes après).

Mon interprétation de connexions entrantes: connexions qui n'ont pas encore été traitées et qui "s'entassent" dans un file d'attente qui elle est limitée.
Oui, c'est ça. On appelle ça des 'demandes de connexions non servies'.
Mais peut être que cette file (sachant que chaque connexion va être traitée "immédiatement" par un thread) ne sera jamais saturée...
Rarement. Il faudrait un afflux massif et simultané...

Pour les threads, comment sait-on qu'on en est au dernier thread possible? On doit gérer une variable compteur? Ou il existe une fonction propre au threads?
Je ne sais pas trop. Il faudrait étudier de près la doc officielle des threads POSIX. Je suppose qu'il y a une fonction pour ça, car la valeur doit changer selon l'architecture, le système etc.

http://www.opengroup.org/onlinepubs/000095399/basedefs/pthread.h.html

-ed-
Admin
Admin

Messages : 289
Date d'inscription : 26/05/2008
Age : 60
Localisation : Paris 6eme arrondissement (75, France)

Voir le profil de l'utilisateur http://bien-programmer.fr

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  chouchou le Mer 5 Nov 2008 - 14:36

Merci -ed- pour ton aide!
Tout est plus clair maintenant :)

chouchou

Messages : 6
Date d'inscription : 03/11/2008

Voir le profil de l'utilisateur

Revenir en haut Aller en bas

Re: Arreter un serveur multiclient

Message  Contenu sponsorisé Aujourd'hui à 21:18


Contenu sponsorisé


Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum