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 :

Problème Seveur multi-thread


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 36
    Par défaut Problème Serveur multi-thread
    Bonjour,

    J'ai reçu un devoir dont vous pouvez avoir l'énnoncé ici.
    Tout d'abord, pour créer un serveur pouvant gérer plusieurs clients je me suis dirigé vers l'idée d'un serveur multi-thread, j'ai vu qu'il y avait aussi une solution consistant à utiliser des sockets asynchrones mais j'ai pas trouvé beaucoup d'info là dessus et ça me semble assez compliqué.
    Donc après avoir décider de faire un serveur multithread, j'ai donc fait des recherche là dessus et j'ai trouvé cet exemple donné dans vos tutaux, je comprend à peu près tout mais j'aurais tout de même besoin d'éclaircissements:

    Tout d'abord les fonctions utilisés là ont-elles le même nom sous linux? Car pour que le programme soit éxécutable sous windows et linux je compte faire un en tête du style:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #if defined (WIN32)
    #include <winsock2.h>
     
    #elif defined (linux)
     
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    (Pour le moment j'ai mis ces bibliothèques pour linux car il semblerait que ce soit les "classiques" pour l'utilisation de sockets)

    Ensuite le passage du code que je n'ai pas bien compris est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    static DWORD WINAPI ThreadLauncher(void *p){
    			struct thread_param *Obj = reinterpret_cast<struct thread_param*>(p);               
    			serveur *s = Obj->ser;                          
    			return s->ClientThread(Obj->soc);       
    		}
    En réalité au final je ne comprend pas bien quelles information contient le socket utilisé par la fonction "ClientThread", je suppose que c'est le socket correspondand au client mais ensuite je ne comprend pas bien comment faire que chaque thread gère son client, et surtout comment le serveur fait pour envoyer un message à tout les clients. Donc un peu d'aide ne serait pas de trop (je trouve que pour notre niveau le prof nous demande vraiment un truc très difficile, surtout qu'on a qu'un mois pour faire ça ).
    Merci d'avances pour vos résponses, explications ou conseils.

  2. #2
    Membre confirmé
    Inscrit en
    Octobre 2006
    Messages
    114
    Détails du profil
    Informations forums :
    Inscription : Octobre 2006
    Messages : 114
    Par défaut probleme serveur multi-thread
    slt,
    moi aussi j'avais le mm probleme que toi le mois dernier. Mais j'ai finalement pu creer ma propre classe Thread ou j'encapsule toutes ses fonctions.
    Le 1er conseil que je te donnerai c'est te lire cet article : http://www.devarticles.com/c/a/Cplus...hreading-in-C/
    il est en Anglais mais vraiment bien. Il te donne le concept du multithreading en C++. Ensuite utilise la classe Thread que j'ai uploader avec ce mail.
    L'idee est de comprendre la fonction accept est bloquante DONC tu NE dois PAS la mettre dans le main. C'est la raison pour laquelle elle doit etre placee dans un thread.
    Pour utiliser la classe que je t'ai passe c'est simple. Par ex suppose que ton network classe s'appelle network.
    Tu fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class network : public Thread 
     { //Tu dois imperative definir la fonction run ();
       int run()
        {
         //Le code principal de ta fonction
        //N'oublie pas de mettre une boucle infinie
       return 0; //normalement tu ne dois jamais arriver a cet etape 
     
      }
     
     
     }
    Cote serveur, tout ce que tu as a faire c'est de garder TOUS les sockets retournes par la fonction ACCEPT quelque part et assigner ces sockets a des clients. Apres avoir recu un nouveau socket de la fonction ACCEPT, TU dois appeller LISTEN encore et puis ACCEPT. Le code ressemble a :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    for (;;) //boucle infinie
    	{
    	int sinsize = sizeof(csin);
    	if((_acceptingsocket = accept(_listeningsocket, (SOCKADDR *)&csin, &sinsize)) != INVALID_SOCKET)
    	{	_numberOfConnectedClients++;
    		//TODO: before putting the brand-new socket into the shared memory area, first ask the incoming client 
    		//to identify itself by receiving a message from him
    		// EX: recv : are you a player or a spectator?
    		 cout <<"Well done ..."<<endl;
    		 cout <<"You are connected to IP : ..."<< /*ntohl (csin.sin_addr.S_un.S_addr)*/ inet_ntoa(csin.sin_addr) <<endl;
    		 //cout <<"On port number..."<<ntohs(csin.sin_port)<<endl;
    		 cout <<"There are "<<_numberOfConnectedClients<<"Connected Clients... "<<endl;
    		 this->_shared.SetSocket(_acceptingsocket);
    	}
     
    	if (listen(_listeningsocket, 5) == SOCKET_ERROR) cerr<<"Fail to listen"<<endl;
    	else cout <<"Listening to connections ... "<<endl;
    	}
    Un autre point IMPORTANT, puisque tu geres plusieurs clients a la fois, TU dois penser au probleme de synchronisation, car 2 clients ne peuvent pas avoir le meme socket. Donc je te renvoie sur cet excellent article de GAMASUTRA : http://www.gamasutra.com/features/20...paquet_pfv.htm
    Voila je crois t'avoir aide autant que possible, n'hesite pas de me poser des questions supplementaires, email : franclin@netcourrier.com (le moyen le plus sur et le plus rapide de m'avoir.)
    @+
    Fichiers attachés Fichiers attachés

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 36
    Par défaut
    Merci bien pour tous ces conseil je vais de ce pas lire tes articles

  4. #4
    Rédacteur

    Avatar de millie
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    7 015
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 7 015
    Par défaut
    Je pense que tu sais que dès que l'on fait intervenir des threads, ça devient très difficile à gérer (notamment les données partagées).

    Il est parfaitement possible de gérer plusieurs clients avec un seul processus/thread en utilisant select. Pourquoi ne pas utiliser cela ? Et utiliser des threads pour des opérations couteuses (le transfert d'un fichier où je ne sais quoi).

    Le code peut alors ressembler simplement à cela (c'est un code C) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    while(1)
      {
        maxfd = fdConnection+1;
        if(maxfd< STDIN_FILENO+1)
          maxfd = STDIN_FILENO+1;
     
        FD_ZERO(&setfd);
        FD_SET(fdConnection, &setfd);
        FD_SET(STDIN_FILENO, &setfd);
     
        /** repositionnement pour chaque client */
     
        clientListBegin();
        for(i = 0; i<clientListGetSize(); i++)
        {
          int lsocket = clientListGetSocket(i);
          FD_SET(lsocket, &setfd);
          if(lsocket+1>maxfd)
            maxfd = lsocket+1;
        }
        clientListEnd();
     
        if(select(maxfd, &setfd, 0,0,0) == -1)
        {
          perror("select");
          exit(EXIT_FAILURE);
        }
     
        if(FD_ISSET(STDIN_FILENO, &setfd))
        {
          fprintf(stderr, "Close program : \n");
          break;
        }
        /*on matte ce que les gens racontent*/
        clientListBegin();
        for(i = 0; i<clientListGetSize(); i++)
        {
          lsocket = clientListGetSocket(i);
          if(FD_ISSET(lsocket, &setfd))
          {
            //tailleget = read(lsocket, buffer, sizeof(buffer)-1);
            tailleget =  recv(lsocket, buffer, sizeof(buffer)-1, MSG_PEEK);
            if(tailleget == -1)
            {
              perror("recv");
              continue;
            }
     
            if(tailleget == 0)
            {
              fprintf(stderr, "Disconnection\n");
              close(lsocket);
              clientListPrepareToRemove(i);
              continue;
            }
     
            char *  realEnd;
     
            realEnd = memchr(buffer, '\n', tailleget);
            if(realEnd == NULL)
            {
              fprintf(stderr, "Server receive a too long packet\n");
              continue;
            }
     
            tailleget = realEnd - buffer + 1;
     
            tailleget = recv(lsocket, buffer, tailleget,0 );
     
     
            if(tailleget == -1)
            { /*n'arrivera jamais ici*/
              perror("recv");
            }
            else
            {
              /*fin de connexion ?*/  /*n'arrivera jamais ici*/
              if(tailleget == 0)
              {
                fprintf(stderr, "Disconnection...\n");
                close(lsocket);
                clientListPrepareToRemove(i);
              }
              else
              {
                buffer[tailleget] = '\0';
                printf(" * PACKET :  : %s\n", buffer);
                serverInterpreter(buffer, lsocket);
                DEBUG("End serverInterpreter");
              }
            }
          }
        }
        clientListEnd();
     
        DEBUG("debut demande de connexion");
     
     
        /* nouvelle demande de connexion ?*/
        if(FD_ISSET(fdConnection, &setfd))
        {
          fprintf(stderr, "Demande de connexion....\n");
          tailleaddr = sizeof addrClient;
          clientSocket = accept(fdConnection, (struct sockaddr*) &addrClient,
                                &tailleaddr);
          if(clientSocket == -1)
          {
            perror("accept");
            continue;
          }
          //fprintf(stderr, " * Port : %d", ntohs(addrClient.sin_port));
     
          if(clientListAdd(clientSocket, addrClient.sin_addr.s_addr, addrClient.sin_port) == -1)
          {
            fprintf(stderr, "Ajout client impossible\n");
          }
        }
     
        DEBUG("fin demande de connexion");
     
      }


    //N'oublie pas de mettre une boucle infinie
    return 0; //normalement tu ne dois jamais arriver a cet etape
    Ton implémentation de ta classe thread m'a l'air très mauvaise si tu ne dois faire une boucle infinie comme ça. En plus, une boucle infinie, c'est une attente active ce qu'il faut ne surtout pas faire !

    L'idee est de comprendre la fonction accept est bloquante DONC tu NE dois PAS la mettre dans le main. C'est la raison pour laquelle elle doit etre placee dans un thread.
    Cet argument n'est absolument pas valabe ! Il suffit d'utiliser select. Ta manière de raisonner suppose que ça peut bloquer dans un thread, donc que le thread dorme, mais qu'il soit toujours là ! C'est une très mauvaise solution. Ton serveur doit être capable de vivre pendant des journées, si tu as des threads qui dorment partout, ou des threads qui sont en attente actives, ça ne va pas le faire.

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 36
    Par défaut
    Le programme doit être compatible windows et linux, vos fonctions le sont-elles? Sinon encore merci pour les réponses. Sinon une question plus basique... Comment on fait pour linker la bibliothèques winsock sur DevC++ ?

  6. #6
    Membre Expert
    Avatar de coyotte507
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    1 327
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 1 327
    Par défaut
    Bonjour,

    Je peux te répondre deux choses:
    • Pour la compatibilité avec linux windows et autres, utilise SDL_net et SDL_thread dont les fonctions marchent partout.
    • Dev-C++ n'est plus mis à jour par sa communauté, donc vaut mieux utiliser CodeBlocks (sinon je connais pas la réponse précise à ta question)


    Je ne sais rien de plus,

    Salut.

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 36
    Par défaut
    J'ai finalement décidé de m'orienter vers cette solution avec le select, mais j'aurais (encore ) plusieurs questions:

    Je me base sur ce anuel:

    http://www.linux-kheops.com/doc/man/.../select.2.html

    Là ils disent que pour les socket il faut écouter le exceptfds pour les socket et toi dans la fontion select tu as mis sur le readfds, qu'est-ce que ça change?

    Je comprend pas trop en fait l'utilisation, serait-ce possible d'avoir plus d'explications, par exemple est-ce que c'est compatible en C++? Je comprend pas l'utilisation de la liste, ça stocke tout les socket des clients? Si la réponse à la question précédente est vraie, serait-il possible de gérer des channel du chat avec un thread utilisant un select pour chaque channel? Comment obtient-on la première valeurdu select (ici maxfd)?
    Voila , merci d'avance pour les futures réponses.

    Citation Envoyé par millie
    Je pense que tu sais que dès que l'on fait intervenir des threads, ça devient très difficile à gérer (notamment les données partagées).

    Il est parfaitement possible de gérer plusieurs clients avec un seul processus/thread en utilisant select. Pourquoi ne pas utiliser cela ? Et utiliser des threads pour des opérations couteuses (le transfert d'un fichier où je ne sais quoi).

    Le code peut alors ressembler simplement à cela (c'est un code C) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    while(1)
      {
        maxfd = fdConnection+1;
        if(maxfd< STDIN_FILENO+1)
          maxfd = STDIN_FILENO+1;
     
        FD_ZERO(&setfd);
        FD_SET(fdConnection, &setfd);
        FD_SET(STDIN_FILENO, &setfd);
     
        /** repositionnement pour chaque client */
     
        clientListBegin();
        for(i = 0; i<clientListGetSize(); i++)
        {
          int lsocket = clientListGetSocket(i);
          FD_SET(lsocket, &setfd);
          if(lsocket+1>maxfd)
            maxfd = lsocket+1;
        }
        clientListEnd();
     
        if(select(maxfd, &setfd, 0,0,0) == -1)
        {
          perror("select");
          exit(EXIT_FAILURE);
        }
     
        if(FD_ISSET(STDIN_FILENO, &setfd))
        {
          fprintf(stderr, "Close program : \n");
          break;
        }
        /*on matte ce que les gens racontent*/
        clientListBegin();
        for(i = 0; i<clientListGetSize(); i++)
        {
          lsocket = clientListGetSocket(i);
          if(FD_ISSET(lsocket, &setfd))
          {
            //tailleget = read(lsocket, buffer, sizeof(buffer)-1);
            tailleget =  recv(lsocket, buffer, sizeof(buffer)-1, MSG_PEEK);
            if(tailleget == -1)
            {
              perror("recv");
              continue;
            }
     
            if(tailleget == 0)
            {
              fprintf(stderr, "Disconnection\n");
              close(lsocket);
              clientListPrepareToRemove(i);
              continue;
            }
     
            char *  realEnd;
     
            realEnd = memchr(buffer, '\n', tailleget);
            if(realEnd == NULL)
            {
              fprintf(stderr, "Server receive a too long packet\n");
              continue;
            }
     
            tailleget = realEnd - buffer + 1;
     
            tailleget = recv(lsocket, buffer, tailleget,0 );
     
     
            if(tailleget == -1)
            { /*n'arrivera jamais ici*/
              perror("recv");
            }
            else
            {
              /*fin de connexion ?*/  /*n'arrivera jamais ici*/
              if(tailleget == 0)
              {
                fprintf(stderr, "Disconnection...\n");
                close(lsocket);
                clientListPrepareToRemove(i);
              }
              else
              {
                buffer[tailleget] = '\0';
                printf(" * PACKET :  : %s\n", buffer);
                serverInterpreter(buffer, lsocket);
                DEBUG("End serverInterpreter");
              }
            }
          }
        }
        clientListEnd();
     
        DEBUG("debut demande de connexion");
     
     
        /* nouvelle demande de connexion ?*/
        if(FD_ISSET(fdConnection, &setfd))
        {
          fprintf(stderr, "Demande de connexion....\n");
          tailleaddr = sizeof addrClient;
          clientSocket = accept(fdConnection, (struct sockaddr*) &addrClient,
                                &tailleaddr);
          if(clientSocket == -1)
          {
            perror("accept");
            continue;
          }
          //fprintf(stderr, " * Port : %d", ntohs(addrClient.sin_port));
     
          if(clientListAdd(clientSocket, addrClient.sin_addr.s_addr, addrClient.sin_port) == -1)
          {
            fprintf(stderr, "Ajout client impossible\n");
          }
        }
     
        DEBUG("fin demande de connexion");
     
      }




    Ton implémentation de ta classe thread m'a l'air très mauvaise si tu ne dois faire une boucle infinie comme ça. En plus, une boucle infinie, c'est une attente active ce qu'il faut ne surtout pas faire !



    Cet argument n'est absolument pas valabe ! Il suffit d'utiliser select. Ta manière de raisonner suppose que ça peut bloquer dans un thread, donc que le thread dorme, mais qu'il soit toujours là ! C'est une très mauvaise solution. Ton serveur doit être capable de vivre pendant des journées, si tu as des threads qui dorment partout, ou des threads qui sont en attente actives, ça ne va pas le faire.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 36
    Par défaut
    Bon, comme j'ai pas trouvé assez d'info sur le select et comme ça à l'air assez compliqué je suis retourné vers les threads, par contre j'aimerai savoir comment extraire des mots à partir d'un message.
    Par exemple la personne tape:
    join Channel1
    Là il faudrai voir qu'il y a "join" et "Channel1" donc ça appelle une foction "ConnectChannel" avec comme paramètre "Channel1", est-ce qu'il y a des fonction sspéciales pour ça?

Discussions similaires

  1. Ubuntu 11.10 sur Vmware : problème de multi-threading?
    Par khealou dans le forum Threads & Processus
    Réponses: 1
    Dernier message: 30/12/2012, 11h33
  2. Problèmes de multi-threading
    Par ToTo13 dans le forum Général Java
    Réponses: 10
    Dernier message: 20/06/2012, 15h36
  3. Réponses: 7
    Dernier message: 12/06/2011, 07h14
  4. Probléme serveur multi-thread
    Par hebus44 dans le forum Entrée/Sortie
    Réponses: 2
    Dernier message: 14/11/2007, 22h32
  5. problme de multi thread
    Par L4BiN dans le forum Concurrence et multi-thread
    Réponses: 22
    Dernier message: 25/04/2007, 16h47

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