Partage
  • Partager sur Facebook
  • Partager sur Twitter

SFML: Petite incompréhension avec SocketSelector.

Sujet résolu
    28 novembre 2014 à 17:34:30

    Bonsoir à tous,
    J'ai repris le code présenté dans la page de documentation concernant SocketSelector. Il est disponible ici même : http://sfml-dev.org/documentation/2.0-fr/classsf_1_1SocketSelector.php#details.
    J'éprouve quelques difficultés à le modifier. C'est la raison pour laquelle ce code est rempli de "printf" indiquant où en est le programme lorsque je l'éxécute.
    Concrètement ce que je cherche à faire dans ce code c'est :
    - connecter N clients à un serveur (ça j'y arrive)
    - faire en sorte que chaque fois qu'un nouveau client se connecte tous les autres utilisateurs soient prévenus. (ça ça marche aussi)
    - faire en sorte que chaque fois qu'un message est envoyé le serveur l'affiche dans sa console (puis à l'avenir qu'il le renvoie vers tous les clients afin de faire un mini-chat).
    Et c'est ce dernier point qui me bloque.
    Le code,une simple adaptation de celui de la page sur SocketSelector, est recopié ici :
    #include <iostream>
    #include <SFML/Network.hpp>
    #include <list>
    
    int main()
    {
        // Create a socket to listen to new connections
        sf::TcpListener listener;
        listener.listen(5000);
        // Create a list to store the future clients
        std::list<sf::TcpSocket*> clients;
        // Create a selector
        sf::SocketSelector selector;
        // Add the listener to the selector
        selector.add(listener);
        // Endless loop that waits for new connections
        while (true)
        {
            // Make the selector wait for data on any socket
            if (selector.wait())
            {
                // Test the listener
                if (selector.isReady(listener))
                {
                    // The listener is ready: there is a pending connection
                    sf::TcpSocket* client = new sf::TcpSocket;
                    if (listener.accept(*client) == sf::Socket::Done)
                    {
                        // Add the new client to the clients list
                        clients.push_back(client);
                        // Add the new client to the selector so that we will
                        // be notified when he sends something
                        selector.add(*client);
                        sf::Packet packet;
                        std::string str = "Serveur : Un nouveau client s'est connecté.\n";
                        printf("%s",str.c_str());
                        packet << str;
                        int i=0;
                        for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
                        {
                            sf::TcpSocket& client = **it;
                            if (!(client.send(packet) == sf::Socket::Done))
                            {
                                printf("Le paquet contenant \"un nouveau client vient de se connecter n'a pas pu être envoyé au client %d\n\"",i);
                            }
                            else printf("Le paquet contenant l'info de nouvelle connexion a bel et bien été envoyé au client %d.\n");
                            i++;
                        }
                        
                    }
                    else
                    {
                        printf("La connexion n'a pas pu être établie.\n");
                        // Error, we won't get a new connection, delete the socket
                        delete client;
                    }
                }
                else
                {
                    // The listener socket is not ready, test all other sockets (the clients)
                    printf("On a reçu des données d'autre chose qu'en provenance du listeneur.\n");
                    int j = 0;
                    for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
                    {
                        sf::TcpSocket& client = **it;
                        if (selector.isReady(client))
                        {
                            printf("On a reçu des paquets venant du client %d\n", j);
                            // The client has sent some data, we can receive it
                            sf::Packet packet;
                            if (client.receive(packet) == sf::Socket::Done)
                            {
                                std::string msg;
                                packet >> msg;
                                std::cout << msg;
                                
                            }
                        }
                        j++;
                    }
                }
            }
        }
    }
    Le scénario est le suivant :
    -Je connecte un premier client à mon serveur. Les printf que j'utilise m'indiquent que la connexion s'est bien produite. J'ai pu découvrir à ce propos que le client envoie deux messages suite à cette connexion. Je soupçonne qu'il s'agisse là d'accusés de réception : Client1 -> Serveur dit : "ok je me suis connecté" puis Client1 -> Serveur dit : "j'ai bien reçu ta chaîne de caractère indiquant qu'un nouveau client s'était connecté" (à savoir le client1 lui-même).
    -J'envoie un message au serveur depuis mon client 1. Rien ne semble se produire.
    -Je connecte un second client à mon serveur. Tout se passe bien. Et une fois ceci fait le message précédemment envoyé s'affiche.
    1) Je ne comprends pas du tout pourquoi le message émis par le client 1 ne s'affiche dans la console du serveur qu'après que le client 2 se soit connecté. Est-ce qu'une personne bien informée pourrait m'en expliquer la raison ?
    2) J'ai constaté que lorsque je quittais inopinément un programme client ( CTRL+C sous unix ) une boucle infinie se lançait dans le serveur. Je suppose que le serveur n'a pas été programmé pour prévoir un cas de déconnexion. Mais je ne comprends pas ce qui le fait entrer dans cette boucle et je ne sais pas comment résoudre ce problème. Pourriez-vous m'aider sur ce point également ?
    Je vous remercie chaleureusement pour votre aide.
    PS : ce message est un doublon d'un autre post publié ici : http://fr.sfml-dev.org/forums/index.php?topic=16873.0
    Mais ce dernier forum semble beaucoup moins actif que celui d'open classroom.

    -
    Edité par Pauda 29 novembre 2014 à 8:46:32

    • Partager sur Facebook
    • Partager sur Twitter
      2 décembre 2014 à 10:05:04

      // Create a selector
      sf::SocketSelector selector;
      // Add the listener to the selector
      selector.add(listener);
      // Endless loop that waits for new connections

      Tu dois recréer le selector à chaque fois que tu veux t'en servir. Donc crée-le dans la boucle.

      • Partager sur Facebook
      • Partager sur Twitter
      Pour ceux qui souhaiteraient apprendre à développer en Rust, un tuto en français est dispo ici. Pour voir mes projets : github.
        2 décembre 2014 à 12:50:38

        Bonjour et merci pour ta réponse.

        J'ai essayé ce que tu viens de proposer mais cela ne fonctionne pas. Et c'est normal : si je réinitialise le selector à chaque tour de boucle, il n'y a aucune raison pour qu'il se souvienne des sockets enregistrés précédemment. Par conséquent mon serveur ne peut pas communiquer avec les clients puisqu'il efface tous les canaux de communications (sockets) avec eux de sa mémoire à chaque tour de boucle.

        De plus l'auteur de la documentation n'a pas mis le selector dans la boucle.

        Je crois donc ne pas comprendre ce que tu veux dire. Pourrais-tu me détailler un peu plus ta pensée ?

        -
        Edité par Pauda 2 décembre 2014 à 12:52:18

        • Partager sur Facebook
        • Partager sur Twitter
          2 décembre 2014 à 14:04:28

          Oups ! En effet je me suis totalement trompé ! J'ai confondu avec autre chose...

          Pour en revenir à ton problème, je t'avoue ne pas vraiment le comprendre. Il te suffit de boucler sur tes clients et d'envoyer le message sur client_courant != client_qui_a_recu_le_message.

          PS: pourquoi utilises-tu printf dans du C++ alors que tu utilises cout à certains endroits ? printf c'est du C, ça n'a rien à faire dans ce code.

          • Partager sur Facebook
          • Partager sur Twitter
          Pour ceux qui souhaiteraient apprendre à développer en Rust, un tuto en français est dispo ici. Pour voir mes projets : github.
            3 décembre 2014 à 0:00:41

            Pour le printf, c'est juste parce que par le passé j'ai plus souvent eu l'occasion de faire du C que du C++ et du coup c'est un vieux réflexe. En effet je peux tout remplacer par des cout.

            En fait ce que je veux comprendre c'est pourquoi ce code ne fait pas ce que je veux.
            Et ce que je veux c'est que dès qu'un client envoie un message le serveur le réceptionne et l'affiche dans sa console.
            Mais en l'occurrence je dois attendre qu'un nouveau client se connecte pour que le serveur affiche le message du premier client dans sa console.
            Je ne m'explique pas ce phénomène. J'ai pas mal essayé de modifier mon code mais rien n'y fait.

            • Partager sur Facebook
            • Partager sur Twitter
              3 décembre 2014 à 1:36:10

              Pauda a écrit:

              En fait ce que je veux comprendre c'est pourquoi ce code ne fait pas ce que je veux.

              Ton code fonctionne, il manque juste un flush, c'est pour ca que ca ne s'affiche pas dessuite.

              ligne 75

              std::cout << msg;

              rajoute un std::flush ou un std::endl

              -
              Edité par Dichotoman 3 décembre 2014 à 1:42:39

              • Partager sur Facebook
              • Partager sur Twitter
                3 décembre 2014 à 8:44:34

                Je te remercie pour ton aide AlienX13 mais ça ne fonctionne toujours pas.
                • Partager sur Facebook
                • Partager sur Twitter
                  3 décembre 2014 à 9:05:47

                  Qu'est ce qui ne fonctionne toujours pas? Avec qu'un seul client j'obtiens:

                  J'ai juste rajouter un std::endl côté serveur, et mon code client est basique:

                  int main()
                  {
                      sf::TcpSocket socket;
                      if(socket.connect("127.0.0.1", 5000) != sf::Socket::Done)
                      {
                          throw std::runtime_error("sf::TcpSocket::connect()");
                      }
                  
                      std::string line;
                      while(std::getline(std::cin, line))
                      {
                          sf::Packet packet;
                          packet << line;
                          if(socket.send(packet) != sf::Socket::Done)
                          {
                              throw std::runtime_error("sf::TcpSocket::send()");
                          }
                      }
                  }

                  Si j'ai plusieurs clients, dès l'envois le serveur affiche le message pas besoin qu'un nouveau client se connecte.

                  -
                  Edité par Dichotoman 3 décembre 2014 à 9:10:36

                  • Partager sur Facebook
                  • Partager sur Twitter
                    4 décembre 2014 à 0:13:00

                    En fait c'était mon client qui avait un problème, pas mon serveur. Je te remercie franchement AlienX13.

                    Toutefois j'ai une dernière question. Quand depuis le logiciel client on veut fermer la connexion, alors comment le faire proprement ? Et si le client quitte subitement (par exemple si je fais CTRL+C dans le client) comment traiter ce problème avec mon serveur ? Actuellement en effet, ça fait juste boucler mon serveur infiniment sur un truc absurde. Réciproquement -mais c'est un cas plus rare en pratique- si le serveur plante, comment traiter le problème proprement depuis le client ? 

                    Merci encore !

                    -
                    Edité par Pauda 4 décembre 2014 à 0:14:28

                    • Partager sur Facebook
                    • Partager sur Twitter
                      4 décembre 2014 à 13:07:45

                      Je suis débutant également donc je ne connais pas la bonne solution.

                      CTRL^C, est interceptable, son signal est SIGINT et tu peux appeler une fonction quand on le recoit. Une fonction qui enverrais aux clients que le serveur va très prochainement se deconnecter (tu peux faire pareil côté client).

                      Si le serveur se deconnecte vraiment sans t'en informer, le seul moyen de le savoir c'est lors d'un sf::TcpSocket::send() ou sf::TcpSocket::receive(),

                      -
                      Edité par Dichotoman 4 décembre 2014 à 13:20:54

                      • Partager sur Facebook
                      • Partager sur Twitter
                        4 décembre 2014 à 13:26:29

                        Lu'!

                        Petit rappel - C++ est un langage à exception, il y a beaucoup trop de pointeur nu dans ce code.

                        • Partager sur Facebook
                        • Partager sur Twitter

                        Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                          4 décembre 2014 à 13:44:53

                          @Pauda: C'est relativement simple en fait, d'apres le man, la fonction recv renvoie un nombre inferieur a 1 si le client est deconnecte (seul moyen de savoir si le client est toujours connecte d'ailleurs). Plus qu'a trouver l'equivalent avec la sfml !
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Pour ceux qui souhaiteraient apprendre à développer en Rust, un tuto en français est dispo ici. Pour voir mes projets : github.
                            4 décembre 2014 à 15:18:55

                            Merci à tous pour vos réponses. C'était très intéressant. Je passe le sujet en résolu.
                            • Partager sur Facebook
                            • Partager sur Twitter

                            SFML: Petite incompréhension avec SocketSelector.

                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                            • Editeur
                            • Markdown