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

Réseau C Discussion :

Problème pour la gestion des clients


Sujet :

Réseau C

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    52
    Détails du profil
    Informations personnelles :
    Âge : 30
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 52
    Points : 29
    Points
    29
    Par défaut Problème pour la gestion des clients
    Bonjour

    alors ça fait un moment que je bosse sur un chat et j'ai un problème étrange que je n'arrive pas à résoudre.
    En effet au bout d'un certain nombre de messages échangés entre les deux clients, le premier client à avoir été connecté au serveur n'arrive plus à envoyer des messages. Le serveur ne les perçoit plus. Mais il arrivent parfois que celui-ci (bien que le serveur ne reçoive plus ses messages) puisse recevoir ceux du client2.

    C'est un peu dure à expliquer, car des fois ça varie, ou après seulement un message du client2 il est impossible de rentrer en communication avec le serveur (j'ai l'impression) comme dans le teste ci-dessous.

    Une photo du dernier teste si ça peut vous aider à visualiser

    Image ici

    Une autre avec le même bug (cas le plus souvent rencontré) le serveur n'a pas reçue le dernier message de clientA, donc bibi non plus. La connexion entre ClientA et le serveur est donc apparemment inexistante à ce moment

    ICI

    Source :

    Serveur :
    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
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
     
    #if defined (WIN32)
        #include <winsock2.h>
        typedef int socklen_t;
    #elif defined (linux)
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <unistd.h>
        #define INVALID_SOCKET -1
        #define SOCKET_ERROR -1
        #define closesocket(s) close(s)
        typedef int SOCKET;
        typedef struct sockaddr_in SOCKADDR_IN;
        typedef struct sockaddr SOCKADDR;
    #endif
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <time.h>
    #include <string.h>
    #define CLIENT_MAX 1000
     
    const char * NomJourSemaine[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};
    const char * NomMois[] = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};
     
    /*--------------PASSWD----------------------------*/
    static void purger(void)
    {
        int c;
     
        while ((c = getchar()) != '\n' && c != EOF)
        {}
    }
     
    static void clean (char *passwd)
    {
        char *p = strchr(passwd, '\n');
     
        if (p)
        {
            *p = 0;
        }
     
        else
        {
            purger();
        }
    }
    /*------------------PASSWD------------------------------*/
    /*--------------PORT-----------------------------------*/
    static void purgerA(void)
    {
        int d;
     
        while ((d = getchar()) != '\n' && d != EOF)
        {}
    }
     
    static void cleanA (char *addr_port)
    {
        char *q = strchr(addr_port, '\n');
     
        if (q)
        {
            *q = 0;
        }
     
        else
        {
            purgerA();
        }
    }
    /*------------------PORT-----------------------------------*/
     
    typedef struct _Local
    {
    SOCKET s_server;
    SOCKET s_client;
    } Local;
     
    void * readAndSend(void * inform); // Fonction pour recevoire et renvoyer les messages des clients
     
    SOCKET s_global[CLIENT_MAX];
    int index_s = 0;
     
    int main(void)
    {
      #if defined (WIN32)
            WSADATA WSAData;
            int erreur = WSAStartup(MAKEWORD(2,2), &WSAData);
        #else
            int erreur = 0;
        #endif
     
      Local * inform; 
      inform = malloc (sizeof(Local)); 
      char passwd[40];
      char addr_port[20];
      long int port = 0;
      char* fin = NULL;
     
      printf("Sur quelle port utilisez-vous ce serveur : ");
      fgets(addr_port, sizeof addr_port, stdin);
      cleanA(addr_port);
      port = strtol(addr_port, &fin, 10);
      printf("le port enregistr\x82 est le %ld", port);
     
      printf("\n\nEntrez le mot de passe d\x82sir\x82 pour le serveur : ");
      fgets(passwd, sizeof passwd, stdin);
      clean(passwd);
      printf("Le mot de passe enregistr\x82 est : %s\n", passwd);
      printf("\n\nSi celui-ci ne vous convient pas, vous pouvez le changer en red\x82marrant\n \ble serveur.\n");
      printf("\n\n\nServeur en cour de fonctionnement !\n");
     
     inform->s_server;
     
            inform->s_server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
     
            if (inform->s_server == INVALID_SOCKET)
    	       {
                printf("La fonction socket a \x82 \bchou\x82.\n");
                getchar();
    	       }
     
            else
            {
                SOCKADDR_IN server;
     
                server.sin_family       = AF_INET;
                server.sin_addr.s_addr  = htonl(INADDR_ANY);
                server.sin_port         = htons(port);
                memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
     
                if (bind(inform->s_server, (SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
    		       {
                    printf("La fonction bind a \x82 \bchou\x82.\n");
                    getchar();
    		       }
     
             else
               {
     
    		        char buffer[100];
                    int n;
     
    	      while(1)
    	        { // Boucle pour accepter un Client.
                    if (listen(inform->s_server, 0) == SOCKET_ERROR)
    	              {
    		           printf("la fonction listen a \x82 \bchou\x82.\n");
    	              }
     
                    else
                    {
                        inform->s_client;
                        SOCKADDR_IN client;
                        socklen_t csize = sizeof(client);
     
                        inform->s_client = accept(inform->s_server, (SOCKADDR *)&client, &csize);
                        s_global[index_s++] = inform->s_client; 
                        if (inform->s_client == INVALID_SOCKET)
    			          {
                             printf("La fonction accept a \x82 \bchou\x82.\n");
    		              }
     
                        else
                        {                        
     
                            printf("Le client %s s'est connect\x82 !\n", inet_ntoa(client.sin_addr));
    						send(inform->s_client, passwd, (int)strlen(passwd), 0);
    			            pthread_t thread; // Thread pour gérer les messages des clients en même temps que la fonction main afin de permettre à d'autre clients de se connecter sans bloquer les communication.
                            pthread_create(&thread, NULL, (void*)readAndSend, inform); 
    		            }			  
    			    }		
    	        }
               }
            }
     
        return 0;
    }						  
     
     
     
    void * readAndSend(void * inform)
    {
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    Local * in = (Local *) inform;
     
    char buffer[1024];
    char buff[1024];
    int n;
     
        while (1) 
        { // boucle de reception et d'envoie des messages.
    	time_t timestamp;
    	struct tm * t;
     
    	timestamp = time(NULL);
    	t = localtime(&timestamp);	
     
            memset(buffer, '\0', sizeof(buffer));
            n = recv(in->s_client, buffer, sizeof(buffer) - 1, 0); 
     
    	if (n > 0)
    	{ // si on reçoie se que le client dit
    	buffer[n] = '\0';
    	printf("%02u/%s/%04u at ", t->tm_mday, NomMois[t->tm_mon], 1900 + t->tm_year);
    	printf("%02u:%02u:%02u -> ", t->tm_hour, t->tm_min, t->tm_sec);
            printf("%s\n", buffer);    		
            sprintf(buff, "%s", buffer);  
     
            int i;
            for (i = 0; i < index_s; i++)
            {
                send (s_global[i], buff, (int)strlen(buff), 0); 
            }   
    	}
     
    	else
    	{ // dnas le cas où un client se déconnecte. On ne recoie rien
    	closesocket(in->s_client); // on ferme sa socket
    	printf("La fonction recv a \x82 \bchou\x82.\n");
        pthread_exit(NULL); // et on ferme son thread. 
    	break;
    	}	
     
        }
    return NULL;
    }
    Désolé pour l'indentation du code

  2. #2
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Bon déja pour commencer, ton code n'est pas bon, désoler de te l'apprendre

    Quand le premier client se connecte, pas de souci vu que ton accept est bloquant tant qu'une connection n'arrive pas. Par contre quand le deuxieme client se connecte, il n'y a pas d'accept, donc forcément sa pose des souci.

    Ensuite je te conseille d'éviter d'utiliser les thread quand cela n'est pas ABSOLUMENT nécessaire.

    Les thread sont une source d'erreur Monumentale.

    Pour la construction d'un chat, je te renvoi sur cette arcticle :
    selection de socket

    Pour conclure, utilise plutot des descripteur de socket via select quand tu dois gérer du multi socket.

    créé une fonction qui va boucler sur le select, recevoir des client, puis envoyer aux clients.

    J'ai déja fait un systeme de serveur C et client C donc si tu veux des exemple, demande moi

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    52
    Détails du profil
    Informations personnelles :
    Âge : 30
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 52
    Points : 29
    Points
    29
    Par défaut
    Citation Envoyé par skeud Voir le message
    Quand le premier client se connecte, pas de souci vu que ton accept est bloquant tant qu'une connection n'arrive pas. Par contre quand le deuxieme client se connecte, il n'y a pas d'accept, donc forcément sa pose des souci.
    Pourtant il y a une boucle autour de accept. et lorsqu'un client se connecte il est redirigé vers un thread qui va gérer ses message. Pendant se temps, la boucle va réécouter si un client se connecte. si l'accept ne marchait pas pour le deuxième client il ne pourrait pas interagir avec le serveur, hors ici ça marche.

    Ensuite je te conseille d'éviter d'utiliser les thread quand cela n'est pas ABSOLUMENT nécessaire.
    ici j'en ai besoin, pendant que la fonction main écoute et accepte les clients, il me faut une fonction qui gère en même temps les autre clients. Sinon ça restera bloqué sur accept.

    Les thread sont une source d'erreur Monumentale.
    Pourtant je vois pas comment gérer ça autrement

    Pour conclure, utilise plutot des descripteur de socket via select quand tu dois gérer du multi socket.
    Je vais regarder la fonction select.

    en faite mon serveur une fois en route écoute les clients et gère en même temps (via un thread pour pas bloquer le programme à cause de la fonction accept) les messages.

    Je vais éditer le premier message pour commenter la source du serveur, car c'est vrais que ça fait bizarre.

  4. #4
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Exact j'avais pas vu ton while(1) (très mauvais d'utiliser sa d'ailleur)

    voila mon conseil avec select:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    while (continuer==1)
    {
      FD_SET(tasocketaccept, &readf);
      //tu boucle sur le ssocket client
      FD_SET(socketdetesclients, &readf);
      if (select(socketmax+1,&readf,NULL,NULL,NULL) < 0)
        continuer=0;
     if (FD_ISSET(tasocketaccept,&readf))
      //fonction qui accept un client
      //tu boucle sur les socket client
      if (FD_ISSET(sockedetestclient,&readf))
       //fonction qui traite les client
    }
    Voila l'idée, si tu veux plus de précision, demande moi

    Et tu vois comme sa, pas besoin de thread, dans 99% des cas, les petits serveurs sont gérer de cette maniere.

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 400
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    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 400
    Points : 23 780
    Points
    23 780
    Par défaut
    Citation Envoyé par AddicTion Voir le message
    Pourtant il y a une boucle autour de accept. et lorsqu'un client se connecte il est redirigé vers un thread qui va gérer ses message. Pendant se temps, la boucle va réécouter si un client se connecte. si l'accept ne marchait pas pour le deuxième client il ne pourrait pas interagir avec le serveur,
    Il faut se méfier avec les threads, spécialement lorsqu'ils fonctionnent sous UNIX. Selon le niveau de partage (sous Linux, avec clone(), ça va du simple PID jusqu'à la création d'un processus distinct), la manière de gérer les handles de fichiers et de sockets ne va pas être la même. Si un de tes threads est trop indépendant par rapport à son père, l'un et l'autre vont partager la même connexion, et le fait de la refermer d'un côté ne la refermera pas de l'autre.

    Bon, ceci dit, avec les Posix Threads, tu ne devrais pas avoir d'ennuis.

    hors ici ça marche.
    « Or ».

    ici j'en ai besoin, pendant que la fonction main écoute et accepte les clients, il me faut une fonction qui gère en même temps les autre clients. Sinon ça restera bloqué sur accept. Pourtant je vois pas comment gérer ça autrement
    Comme on l'a dit plus haut, select() sert à se mettre à l'écoute de plusieurs descripteurs et à se débloquer dès que l'on d'eux devient disponible pour une action donnée. C'est ce qu'il faut utiliser.

    Les threads semblent être une solution propre et élégante pour gérer les accès multi-utilisateurs. Ce n'est vrai que dans une certaine mesure. Certes, c'est facile pour le programmeur et si l'un des clients meurt à cause d'une exception inattendue, seul le thread plante et le reste du serveur reste debout.

    Par contre, il est nécessaire de savoir faire un serveur multi-utilisateur sans threads, d'une part parce que les machines sur lesquelles tu vas travailler ou faire fonctionner ton serveur ne seront pas forcément multi-tâches elles-mêmes. Ensuite, parce que la solution à un thread par utilisateur est dramatique en termes de montée en charge. Imagine que tu programmes un vrai serveur IRC centralisé, que tu aies un minimum de 200 clients connectés en permanence, et 200 autres qui passent leur temps à se connecter et se déconnecter (les arrivants remplaçant ceux qui partent). Outre l'empreinte mémoire que représenteraient tous ces processus, le système passerait son temps à les nettoyer et à en créer de nouveaux.

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    52
    Détails du profil
    Informations personnelles :
    Âge : 30
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 52
    Points : 29
    Points
    29
    Par défaut
    ok merci pour ces éclaircissements .

    J'ai vaguement vu la fonction select
    @skeud : j'ai du mal à bien cerner le code que tu as posté. Enfin surtout aux lignes 4 et 10

  7. #7
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Imaginons que tu as une liste chainée qui contient tes client:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct s_client
    {
      int socket;
      char *receive;
      struct s_client *next;
    };
    pour la ligne 4:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct s_client *ptr=tastructuredejarempli;
    while (ptr != 0)
    {
      FD_SET(ptr->socket,&readf);
      ptr=ptr->next;
    }
    et pour la ligne 10:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct s_client *ptr=tastructuredejarempli;
    while (ptr != 0)
    {
      if (FD_ISSET(ptr->socket,&readf))
      {
          tafonctionquigerelessocketclient(ptr);
      }
      ptr=ptr->next;
    }
    Je vais essayer de te retrouver un fichier que j'ai coder pour faire sa, et je te le post dès que possible, ça sera plus claire comme sa

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    52
    Détails du profil
    Informations personnelles :
    Âge : 30
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 52
    Points : 29
    Points
    29
    Par défaut
    Ok merci en attendant je vais réviser les listes chainées.

Discussions similaires

  1. Réponses: 5
    Dernier message: 25/05/2010, 08h23
  2. Quel langage pour une gestion des stocks-client-caisse ?
    Par plex dans le forum Langages de programmation
    Réponses: 2
    Dernier message: 07/04/2007, 18h56
  3. Réponses: 2
    Dernier message: 11/05/2005, 13h23
  4. quel SGBD possible pour telle gestion des droits
    Par meufeu dans le forum Décisions SGBD
    Réponses: 11
    Dernier message: 14/04/2005, 09h17
  5. Réponses: 3
    Dernier message: 04/08/2004, 19h48

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