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 :

Fonction select et mise à jour du fd_set


Sujet :

Réseau C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut Fonction select et mise à jour du fd_set
    Bonjour à tous,

    Je dois développer une petite messagerie instantanée en mode console, en utilisant la fonction select.

    Mon problème se trouve au niveau du serveur. Celui-ci doit accepter de nouvelles connexions et lors de la réception d'un message client, le transmettre à tous les autres clients. Le problème est que lorsque je connecte un client puis un deuxième, les messages du premier client ne sont plus détectés...

    Voici le bout de code qui semble être en cause :

    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
    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);
    fd_max = sockfd;
     
    while (1)
    {
     
        switch (select(fd_max + 1, &readfds, NULL, NULL, NULL))
        {
     
            case -1:
            {
        	    perror("select");
        	    return EXIT_FAILURE;
        	}    		
     
        	case 0:
        	    break;
     
        	default:
        	{
     
        	    int i, j;
     
        	    // Sent message
    	    // Searching sender
        	    for (i = 0; i < num_clients; i++)
        	    {
     
       	        if (FD_ISSET(clients[i].sockfd, &readfds))
       		{
     
       		    // Getting message
       		    while ((n = recv(clients[i].sockfd, buffer, sizeof(buffer) - 1, 0)) > 0)
       		    {
     
       		        buffer[n] = '\0';
     
       		        printf("message recu du client %d : %s", i, buffer);
     
       		        // Sending this message to others
       		        for (j = 0; j < num_clients; j++)
       		        {
       		            if (i != j)
       			    {
       			        if (send(clients[j].sockfd, buffer, n, 0) == -1)
       			        {
       			            perror("send");
       				    return EXIT_SUCCESS;
       				}
       			    }
       			}
     
       			// Breaking if message was complete
       			if (n < sizeof(buffer - 1))
       			    break;
     
       		    }
     
       		    // Error during reception
       		    if (n == -1)
       		    {
       		        perror("recv");
       		        close(sockfd);
       			return EXIT_FAILURE;
       		    }
     
        		}
     
        	    }
     
        	    // New client
        	    if (FD_ISSET(sockfd, &readfds) && num_clients < MAX_USERS)
        	    {
     
        	        // Accepting connection
        	        if ((clients[num_clients].sockfd = accept(sockfd, (struct sockaddr *)(&clients[num_clients].addr), &sin_size)) == -1)
        		{
        		    perror("accept");
        		    close(sockfd);
        		    return EXIT_FAILURE;
        		}
     
        		// Updating fd_set and fd_max
        		FD_SET(clients[num_clients].sockfd, &readfds);
        		if (clients[num_clients].sockfd > fd_max)
        		    fd_max = clients[num_clients].sockfd;
     
        		printf("Nouvelle connexion : %d\n", clients[num_clients].sockfd);
     
        		num_clients++;
     
        	    }
     
        	    break;	
     
        	}
     
        }
     
    }
    C'est la première fois que j'utilise cette fonction select, pouvez-vous me dire ce qui ne va pas ?

    Merci !

  2. #2
    Membre très actif
    Avatar de FloMo
    Homme Profil pro
    Freelance iOS
    Inscrit en
    Juillet 2004
    Messages
    726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations professionnelles :
    Activité : Freelance iOS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juillet 2004
    Messages : 726
    Par défaut
    Il faut réinitialiser le fd_set dans ta boucle et non pas à l'extérieur car select() modifie le contenu de ton fd_set.

  3. #3
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut
    Merci pour ta réponse ! Mais ça ne marche pas mieux

    En fait lorsque je connecte mes deux clients, ça fonctionnera pour le premier qui écrira (le message est bien reçu par le serveur puis envoyé vers l'autre client), mais la fonction select reste bloquée lorsque le second client envoie des messages...

  4. #4
    Membre très actif
    Avatar de FloMo
    Homme Profil pro
    Freelance iOS
    Inscrit en
    Juillet 2004
    Messages
    726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations professionnelles :
    Activité : Freelance iOS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juillet 2004
    Messages : 726
    Par défaut
    Arf. Au temps pour moi, je n'avais pas vu le reste du code.

    Fait une boucle avec une socket pour les connexions et lors du accept(), fait un fork() ou un thread pour gérer la communication avec une socket spécifique.

    Donc ta socket de connexion sera réinitialisée dans la boucle du programme principale et sera remise en attente sur un nouveau accept.

    Pendant ce temps là, ton sous-processus issu du fork() ou ton thread fait son petit bonhomme de chemin avec la socket de communication. Dès lors, tu peux utiliser ton select() à volonté.

    Structure bien ton code avec des fonctions simples et efficaces, ça va faciliter la conception et la lecture de ton code.

  5. #5
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par FloMo Voir le message
    Fait une boucle avec une socket pour les connexions et lors du accept(), fait un fork() ou un thread pour gérer la communication avec une socket spécifique.

    Donc ta socket de connexion sera réinitialisée dans la boucle du programme principale et sera remise en attente sur un nouveau accept.

    Pendant ce temps là, ton sous-processus issu du fork() ou ton thread fait son petit bonhomme de chemin avec la socket de communication. Dès lors, tu peux utiliser ton select() à volonté.

    Structure bien ton code avec des fonctions simples et efficaces, ça va faciliter la conception et la lecture de ton code.
    select() + threads/fork(), c'est pas un peu ceinture et bretelle ?

    Personnellement, je n'aime pas select() que je trouve brouillon, complexe et non portable...

    J'utilise donc les threads (et pas fork() pour la même raison), et donc pas select() qui est rendu inutile, (sauf éventuellement pour gérer un timeout...)

    Le thread principal du serveur est une boucle sur accept(). Chaque nouvelle connexion crée alors son thread qui a sa vie autonome (boucle sur recv()). Il gère la déconnexion correctement en surveillant le cr de recv() et de send()..

    En principe, pas besoin de select().

  6. #6
    Membre très actif
    Avatar de FloMo
    Homme Profil pro
    Freelance iOS
    Inscrit en
    Juillet 2004
    Messages
    726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations professionnelles :
    Activité : Freelance iOS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juillet 2004
    Messages : 726
    Par défaut
    Citation Envoyé par Emmanuel Delahaye Voir le message
    select() + threads/fork(), c'est pas un peu ceinture et bretelle ?
    C'est vrai qu'en y réfléchissant à 2 fois, le select() n'a aucun intérêt dans ce contexte... hormis embrouiller le code.

  7. #7
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut
    Je suis complètement d'accord avec vous, je me suis toujours passé de cette fonction mais je fais ce programme dans le cadre d'un exercice sur la fonction select justement...

    Et je ne vois toujours pas ce qui cloche ici (j'ai modifié le programme ci-dessus pour réinitialiser le fd_set à chaque tour de boucle comme me l'a conseillé FloMo).

  8. #8
    Membre très actif
    Avatar de FloMo
    Homme Profil pro
    Freelance iOS
    Inscrit en
    Juillet 2004
    Messages
    726
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Deux Sèvres (Poitou Charente)

    Informations professionnelles :
    Activité : Freelance iOS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juillet 2004
    Messages : 726
    Par défaut
    Généralement, j'utilise read() et write() et il faut que je fasse une lecture sur la socket après chaque écriture. Ca m'a posé problème une fois. Tout dépend de ton environnement de développement.

  9. #9
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 762
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 762
    Par défaut
    Bonjour
    Il serait bien que vous postiez le code modifié pour qu'on comprenne ce qui se passe.
    En regardant la discussion j'ai trouvé cet exemple chez IBM.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  10. #10
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut
    Effectivement, je me suis aperçu que les clients ne lisaient pas tout de suite les messages envoyés par le serveur, ce qui posait problème !

    Le bout de code du serveur ci-dessous fonctionne donc a priori très bien, merci pour votre aide !

    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
    fd_max = sockfd;
     
    while (1)
    {
     
        FD_ZERO(&readfds);
        FD_SET(sockfd, &readfds);
        for (i = 0; i < num_clients; i++)
            FD_SET(clients[i].sockfd, &readfds);
     
        switch (select(fd_max + 1, &readfds, NULL, NULL, NULL))
        {
     
        	case -1:
        	{
                perror("select");
        	    return EXIT_FAILURE;
        	}    		
     
        	case 0:
        	    break;
     
        	default:
        	{
     
        	    // Sent message
    	    // Searching sender
        	    for (i = 0; i < num_clients; i++)
        	    {
     
       	        if (FD_ISSET(clients[i].sockfd, &readfds))
       		{
     
       		    // Getting message
       		    while ((n = recv(clients[i].sockfd, buffer, sizeof(buffer) - 1, 0)) > 0)
       		    {
     
       		        buffer[n] = '\0';
     
       			// Sending this message to others
       			for (j = 0; j < num_clients; j++)
       			{
       			    if (i != j)
       			    {
       			        if (send(clients[j].sockfd, buffer, n, 0) == -1)
       				{
       				    perror("send");
       				    return EXIT_FAILURE;
       				}
       			    }
       			}
     
       			// Breaking if message was complete
       			if (n < sizeof(buffer) - 1)
       			    break;
     
       		    }
     
       		    // Error during reception
       		    if (n == -1)
       		    {
       		        perror("recv");
       			close(sockfd);
       			return EXIT_FAILURE;
       		    }
     
        		}
     
        	    }
     
        	    // New client
        	    if (FD_ISSET(sockfd, &readfds) && num_clients < MAX_USERS)
        	    {
     
        	        // Accepting connection
        		if ((clients[num_clients].sockfd = accept(sockfd, (struct sockaddr *)(&clients[num_clients].addr), &sin_size)) == -1)
        		{
        		    perror("accept");
        		    close(sockfd);
        		    return EXIT_FAILURE;
        		}
     
        		// Updating fd_set and fd_max
        		if (clients[num_clients].sockfd > fd_max)
        		    fd_max = clients[num_clients].sockfd;
     
        		num_clients++;
     
        	    }
     
        	    break;	
     
        	}
     
        }
     
    }
    Edit : il fonctionne bien sauf si le message reçu a exactement la taille sizeof(buffer) - 1, dans ce cas le serveur se mettra en attente de réception de donnée sur la socket (bloquante)...Il faut rendre les appels à recv non bloquants (MSG_DONTWAIT) pour éviter cela.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [WD14] Fonction générique de mise à jour de fichier
    Par rndhnqz dans le forum HyperFileSQL
    Réponses: 1
    Dernier message: 07/01/2010, 12h03
  2. [2007] Que pensez-vous des nouvelles fonctions de la mise à jour SP2 d'Office 2007
    Par Invité dans le forum Microsoft Office
    Réponses: 37
    Dernier message: 18/06/2009, 16h50
  3. select et mise à jour du timeout ?
    Par contremaitre dans le forum POSIX
    Réponses: 2
    Dernier message: 25/11/2008, 08h24
  4. [Débutant][<html:select>]options mises à jour dynamiquement
    Par anayathefirst dans le forum Struts 1
    Réponses: 10
    Dernier message: 23/01/2007, 21h24
  5. Réponses: 7
    Dernier message: 15/01/2007, 18h18

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