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 :

Classes amies ou pas et Thread


Sujet :

C++

  1. #1
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut Classes amies ou pas et Thread
    Bonjour à tou(te)s,

    J'ai un souci que je n'arrive vraiment pas à régler et plus je m'y essaye, plus j'ai l'impression de ne pas voir l'évidence.
    Je cherche donc à faire un client Telnet. Pour cela, j'ai donc une classe Telnet qui me permet de me connecter à un serveur. Pour bien recevoir les données du serveur je lance un Thread qui va vérifier si j'ai recu des données de temps en temps. Pour info, j'utilise la SFML tout ca :
    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
    class Telnet
    {
    public :
        //Constructeur par défaut
        Telnet(void);
        //Connexion à un site
        void Connect(const std::string& host, unsigned short port = 23);
        //Pour vérifier qu'on est bien connecté
        bool IsConnected(void);
     
    private :
        //Les informations de connexion
        std::string     mHostName;
        sf::IPAddress   mHost;
        unsigned short  mPort;
        //La socket permettant de recevoir et d'envoyer des données
        sf::SocketTCP   mConnection;
        //Booléen permettant de savoir si on est toujours connecté ou pas
        bool            mIsConnected;
        //Le thread de réception des données envoyées par le serveur
        ReceptionThread mReceptionThread;
    };
    La methode lance donc le Thread qui est une classe dérivée afin qu'il fasse bien ce que je souhaite (méthode virtuelle redéfinie) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class ReceptionThread : public sf::Thread
    {
    private:
        virtual void Run ()
        {
            std::cout<<"Deconnexion"<<std::endl;
            mIsConnected = false;
        }
    };
    Bon, ici ce n'est qu'un test, donc je déconnecte tout de suite. Voici le code du main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int main()
    {
        Telnet t;
     
        t.Connect("crawl.akrasiac.org");
     
        while (t.IsConnected())
        {
            //On attend sans rien faire
        }
     
        return EXIT_SUCCESS;
    }
    Mes soucis :
    - Soit je définis une classe avant l'autre et j'ai des soucis puisque l'autre n'est pas encore définie. Je sais qu'il faut définir partiellement la première afin de ne pas faire d'erreur, mais je n'arrive plus à retrouver comment on fait :/
    - De plus, l'accès à un membre privé, c'est moyen. Je ne veux, bien entendu, pas avoir de setter public ca n'a rien à faire dans l'interface.

    J'ai tenté de définir ReceptionThread dans la classe Telnet en privé puisque l'utilisateur ne devrait pas y avoir accès et de rendre celle-ci amie de la classe Telnet, masi ca ne marche pas :
    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
    class Telnet
    {
    public :
        //Constructeur par défaut
        Telnet(void);
        //Connexion à un site
        void Connect(const std::string& host, unsigned short port = 23);
        //Pour vérifier qu'on est bien connecté
        bool IsConnected(void);
     
    private :
        friend class ReceptionThread;
     
        class ReceptionThread : public sf::Thread
        {
        private:
            virtual void Run ()
            {
                std::cout<<"Deconnexion"<<std::endl;
                mIsConnected = false;
            }
        };
     
        //Les informations de connexion
        std::string     mHostName;
        sf::IPAddress   mHost;
        unsigned short  mPort;
        //La socket permettant de recevoir et d'envoyer des données
        sf::SocketTCP   mConnection;
        //Booléen permettant de savoir si on est toujours connecté ou pas
        bool            mIsConnected;
        //Le thread de réception des données envoyées par le serveur
        ReceptionThread mReceptionThread;
    };
    Citation Envoyé par Debug
    error: invalid use of nonstatic data member 'Telnet::mIsConnected'
    Merci d'avance à ceux (celles ?) qui auraient des (une au moins ?) idées !

    PS : post identique sur le SdZ, mais ils répondent pas
    Mindiell
    "Souvent, femme barrit" - Elephant man

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : juin 2006
    Messages : 1 294
    Points : 1 516
    Points
    1 516
    Par défaut
    Salut,

    Citation Envoyé par Mindiell Voir le message
    Soit je définis une classe avant l'autre et j'ai des soucis puisque l'autre n'est pas encore définie. Je sais qu'il faut définir partiellement la première afin de ne pas faire d'erreur, mais je n'arrive plus à retrouver comment on fait :/
    Comme ça : Comment créer 2 classes qui font référence l'une à l'autre ?


    Citation Envoyé par Mindiell Voir le message
    De plus, l'accès à un membre privé, c'est moyen. Je ne veux, bien entendu, pas avoir de setter public ca n'a rien à faire dans l'interface.
    Une méthode Disconnect() me semblerait pas mal, non ?

    MAT.

  3. #3
    Membre expert
    Profil pro
    Inscrit en
    mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : mars 2007
    Messages : 1 415
    Points : 3 132
    Points
    3 132
    Par défaut
    Salut

    Citation Envoyé par Mindiell Voir le message
    Mes soucis :
    - Soit je définis une classe avant l'autre et j'ai des soucis puisque l'autre n'est pas encore définie. Je sais qu'il faut définir partiellement la première afin de ne pas faire d'erreur, mais je n'arrive plus à retrouver comment on fait :/
    - De plus, l'accès à un membre privé, c'est moyen. Je ne veux, bien entendu, pas avoir de setter public ca n'a rien à faire dans l'interface.
    C'est même plus subtil que ça, car tu veux mettre en place un accès concurrent sur une même donnée : mIsConnected. Il faut donc que le code qui modifie cette valeur soit critique.

    Ton souci est que ReceptionThread doit demander à Telnet de modifier une de ses données, sans l'exposer publiquement. Tu pourrais par exemple stocker une référence vers cette valeur à la construction de RéceptionThread. Attention aux accès concurrents.

    Celà dit, une méthode Disconnect() ne me paraît pas choquante. Pourquoi ne pas vouloir l'exposer ?
    Find me on github

  4. #4
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut
    Yop,

    Merci pour vos réponses. Pour ce qui concerne les classes c'était déjà résolu (merci http://www.ethicle.fr/ ).

    Pour le problème qui m'importe le plus :
    - Disconnect n'a rien à faire en public dans le sens où ce n'est pas à l'utilisateur de la classe de se déconnecter
    - Le fait que mIsConnected est critique est vrai, mais il s'agit d'un booléen et une opération sur un booléen est atomique. Au pire, ca passera une ou deux boucles de plus : pas grave. Quand bien même, il est prévu l'usage d'un mutex pour la suite (remplissage du buffer, etc...), mais là n'est pas mon souci aujourd'hui
    - Donner une référence de la valeur lors de la construction du Thread n'est pas idiot du tout

    Après test, j'ai essayé de passer une référence, mais ca ne marche pas, il semble que ce soit plutôt une copie. En utilisant un pointeur, c'est bon.
    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
    class Telnet
    {
    public :
        //Constructeur par défaut
        Telnet(void);
        //Connexion à un site
        void Connect(const std::string& host, unsigned short port = 23);
        //Pour vérifier qu'on est bien connecté
        bool IsConnected(void);
     
    private :
        class ReceptionThread : public sf::Thread
        {
        public:
            void SetReference (bool* connected) { mConnected = connected; }
        private:
            virtual void Run();
     
            bool* mConnected;
        };
     
        //Les informations de connexion
        std::string     mHostName;
        sf::IPAddress   mHost;
        unsigned short  mPort;
        //La socket permettant de recevoir et d'envoyer des données
        sf::SocketTCP   mConnection;
        //Booléen permettant de savoir si on est toujours connecté ou pas
        bool            mIsConnected;
        //Le thread interne à la classe
        ReceptionThread mReceptionThread;
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void Telnet::ReceptionThread::Run()
    {
        std::cout<<"Deconnexion"<<std::endl;
        (*mConnected) = false;
    }
     
    Telnet::Telnet (void)
    {
        mIsConnected = false;
        //Ici je lie le mConnected de mon Thread au mIsConnected de ma classe
        mReceptionThread.SetReference(&mIsConnected);
    }
    Mindiell
    "Souvent, femme barrit" - Elephant man

  5. #5
    Membre expérimenté

    Profil pro
    Inscrit en
    juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : juin 2006
    Messages : 1 294
    Points : 1 516
    Points
    1 516
    Par défaut
    Citation Envoyé par Mindiell Voir le message
    une opération sur un booléen est atomique.
    Non du tout, rien dans la norme ne garantit ça.

    Citation Envoyé par Mindiell Voir le message
    Après test, j'ai essayé de passer une référence, mais ca ne marche pas, il semble que ce soit plutôt une copie. En utilisant un pointeur, c'est bon.
    Si tu passes et que tu stockes une référence ça fonctionne.
    D'ailleurs tu devrais la passer dans un constructeur de ReceptionThread ça serait plus simple et plus sûr dans l'absolu.


    A part ça pourquoi est-ce que Telnet n'implémente pas sf::Thread avec un héritage privé ?


    MAT.

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 524
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 524
    Points : 29 972
    Points
    29 972
    Par défaut
    Salut,

    La première question qu'il faudrait te poser afin de savoir s'il y a une relation d'héritage entre ReceptionThread et Telnet est "est-ce que ReceptionThread est sémantiquement un objet Telnet", dont la question est, définitivement, non...

    Par contre, la classe ReceptionThread utilisera *forcément* un objet de type Connection qui pourrait, selon le cas, être une connexion Telnet ou FTP ou ... va savoir quoi...

    Et les attendus de la part de ta classe ReceptionThread n'ont, en définitive, que peu à voir avec les services que tu peux attendre d'une classe Connection ou dérivée...

    Au pire, tu peux vouloir demander à ton objet de type ReceptionThread si la connexion existe (comprend: est fonctionnelle), peut-être de disposer du nom (ou de l'ip) du serveur auquel on est connecté et éventuellement pouvoir lui demander, au départ d'un objet d'un type particulier, de tout faire en sorte pour... que la connexion soit effective.

    De plus demeter nous dis que ce n'est pas parce que ta classe ReceptionThread utilise en interne une classe Connection (ou ici Telnet) que tu dois *forcément* permettre de récupérer un objet Connection (Telnet) au départ d'un objet ReceptionThread.

    Une aggrégation normale pourrait donc parfaitement faire l'affaire en ayant un pointeur sur Connection (ou Telnet) comme membre privé de ReceptionThread et en n'exposant qu'une partie spécifique de l'interface de Connection (Telnet) dans ta classe ReceptionThread.

    Dés lors, les fonction connect, disconnet et autres utiles pour la gestion de la connexion lorsque tu travaille sur une instance de Connection (Telnet) peuvent parfaitement rester publiques lorsque tu dispose effectivement de l'instance, mais être purement et simplement inaccessibles depuis une instance de type ReceptionThread (ou uniquement utilisée par le biais d'une éventuelle fonction reconnect()/reinitialize() )

    Tu n'a pas forcément d'amitié à déclarer sur ce point, et tu peux "t'en foutre royalement" que les fonctions connect() ou disconnect() de ta classe Connection (Telnet) soient publique, parce que... tu n'a de toutes manières pas moyen de récupérer une instance de Connection (Telnet) au départ d'une instance de ReceptionThread

    Au final, tu aurais quelque chose proche de (je pars ici du principe que Telnet est une spécialisation de Connection, de manière à pouvoir envisager de créer des classes FTP, HTTP ou Irc correspondant à d'autres protocoles que tu voudrais éventuellement ajouter par la suite )
    Connection.h
    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
    Connection
    {
        public:
            Connection(std::string const & );
            Connection(IP const & );
            virtual ~Connection();
            /* tout cela fait partie de l'interface publique que l'on attend
             * d'un objet Connection
             */
            virtual void connect()=0;
            virtual void disconnect() = 0;
            virtual void identify() = 0;
            /* permet d'envoyer une requête et de récupérer
             * la réponse envoyée
            virtual void  send(Request const &) = 0 const;
           /* permet de disposer de l'ip et du nom de l'hôte cible */
           Ip const & hostIp() const{return m_ip;}
           std::string const & hostName(){return m_hostname;}
           virtual Response const & lastResponse() const;
           /* une fonction permettant de savoir si la connexion est active */
           bool isActive() const{return m_sok->active();}
        protected:
            Response * m_rep;
            Socket * m_sok;
        private:
            Ip m_ip;
            std::string m_hostname;
    };
    La classe Telnet dérive de Connection... mais si tu veux juste un client telnet, tu peux transférer les différents membres et fonctions de Connection directement dans Telnet
    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
    Telnet.h
    #include <Connection.h>
    class Telnet : public Connection
    {
        public:
            Telnet (std::string const &);
            Telnet (IP const & hip);
            virtual ~Telnet();
            virtual void connect();
            virtual void disconnect();
            virtual void identify();
            virtual void  send(Request const &) const;
            /* on peut profiter du retour co variant :D */
           virtual TelResponse const & lastResponse() const;
     
    };
    Telnet.cpp
    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
    #include <Telnet.h>
    Telnet::Telnet(std::string const & hn):Connection(hn)
    {
        connect();
        identify();
    }
    Telnet::Telnet(Ip const & hip):Connection(hip)
    {
        connect();
        identify();
    }
    Telnet::~Telnet()
    {
        if(active())
            disconnect();
    }
    void Telnet::connect()
    {
        /* création d'une connection telnet selon protocole */
    }
    void Telnet::identifiy()
    {
        /* identification auprès d'un serveur telnet selon protocole */
    }
    void Telnet::disconnect()
    {
        /* déconnexion du serveur telnet selon protocole */
    }
    void Telnet::send(Request const & req)
    {
        /* il faut peut être convertir la requete en "requete Telnet"
        TelnetRequest real=static_cast<TelnetRequest const &>(req);
        /* envoi de la requete et création de la réponse */
        m_rep= new TelnetRespons(msock->send(real));
    }
    TelnetResponse const & Telnet::lastResponse() const
    {
        return static_cast<TelnetResponse const &>(*m_rep);
    }
    Et enfin, nous avons la classe ReceptionThread qui profite d'une aggrégation de la classe Telnet:
    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
    ReceptionThread.h
    #include <TelnetRequest.h>
    #include <queue>
    class Telnet;
    class TelnetResponse;
    ReceptionThread : public sf::Thread
    {
        friend class ThreadFactory;
        public:
            ~ReceptionThread() 
            bool connectionActive() const;
            void reinitializeConnection;
            vois programRequest(TelnetRequest const & rq)
            {
                m_req.push(rq);
            }
            void run();
            std::string const & telnetHostname() const;
            Ip const & telnetIp() const;
            TelnetResponse const & lastResponse() const;
    private:
        ReceptionThread(Telnet * con):m_con(con){}
        std::queue<TelnetRequest> m_req;
    };
    ReceptionThread.cpp
    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
    #include <ReceptionThread.h>
    #include <Telnet.h>
    #include <TelnetResponse>
    ReceptionThread ::~ReceptionThread()
    {
        delete m_con;
    }
    bool ReceptionThread :connectionActive() const
    {
        return m_con && m_con->active();
    }
    void ReceptionThread::reinitializeConnection
    {
        if(m_con)
        {
            /* récupération de l'ip ou du nom d'hote */
            delete m_con;
            m_con=new Telnet(/* ip ou nom d'hote */);
        }
    }
    void ReceptionThread:: run()
    {
        if(!connectionActive())
            reinitializeConnection();
        while(!m_req.empty())
        {
            m_con->send(m_req.fron());
              m_req.pop();
        }  
    }
    std::string const & ReceptionThread::telnetHostname() const
    {
        if(!m_con)
            throw ConnectionError();
        return m_con->hostName();
    }
    Ip const & ReceptionThread::telnetIp() const
    {
        if(!m_con)
            throw ConnectionError();
        return m_con->hostIp();
    }
    TelnetResponse const & ReceptionThread::lastResponse() const
    {
        if(!m_con)
            throw ConnectionError();
        return m_con->lastResponse();
    }
    Bon, j'ai laissé de coté tout l'aspect ayant trait à la gestion des threads, mais tu remarquera que les seules choses qui aura réellement besoin de connaitre la classe Telnet sont... la classe ReceptionThread et accessoirement la fabrique de ReceptionThread (qui en fait, ne devra connaitre que... la fabrique de Connection )

    Tu peux donc décemment te dire que l'interface publique de ta classe Telnet peut être aussi fournie que nécessaire pour lui permettre de fournir les services que l'on attend d'elle sans pour autant que les classes qui manipuleraient une classe qui l'utilise ne puisse accéder à tout et à n'importe quoi
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut
    Wouch ! Merci à tous les deux...

    @Mat007 :
    - Si l'opération n'est pas atomique, ce n'est de toute facon pas important dans mon cas. Je mettrai cette partie en critique dès que j'aurai avancé car il ne s'agira plus d'un simple booléen à remplir.
    - Je pense aussi qu'une référence devrait fonctionner, je vais tester cela.
    - Je ne peux pas passer Telnet dans le constructeur vu que l'objet est construit au moment de sa déclaration, non ?
    - Enfin, Telnet n'est pas un Thread, je ne vois pas pourquoi elle hériterait de Thread

    @koala01 :
    - Euh, toute ta réponse me parait un poil complexe à comprendre. Je l'ai déjà relu, mais j'ai un peu de mal. Je ne crois pas m'être bien exprimé quant à mon besoin :
    Les classes Http et Ftp existent déjà dans la SFML je n'ai donc besoin que de Telnet. Mon problème avec Telnet, c'est qu'il s'agit d'un protocole à 2 niveaux je dirais : Il y a les "commandes protocolaires" d'un côté (configuration d uterminal et des échanges à venir (codes ASCII, vitesse, taille de l'écran, etc...) et les "données" de l'autre. Normalement, les "données" sont simplement affichées à l'écran et les "commandes" sont invisibles pour l'utilisateur. Le login, et ce qui suit font partie des "données". C'est donc un peu particulier, suivant ce que je recois, je dois traiter une commande ou renvoyer les données.
    Dans mon cas, je veux que l'utilisateur de la classe puisse "faire ce qu'il veut" avec les données. Dans ce cas, je dois traiter les commandes en invisible et donner un accès vers les données.

    Ainsi, l'utilisateur doit utiliser une classe Telnet comme il utiliserait une classe Http. Les "commandes" doivent être invisibles, mais il doit avoir l'impression de recevoir les données. D'où l'utilisation d'un thread dans la classe qui recoit tous les octets et les traite en tant que commande ou bien les stocke dans une mémoire auquel l'utilisateur aura accès (dans la classe Telnet donc).

    Bref, tout cela pour bien expliquer le pourquoi de ma conception objet :
    Telnet est la classe à utiliser, et ReceptionThread n'est qu'un Thread permettant de séparer les octets reçus en commande ou en données. L'utilisateur de Telnet ne doit pas en avoir conscience. Ainsi, ReceptionThread n'est une classe utile que pour Telnet. Je t'accorde que Disconnect peut être visible depuis Telnet afin que le client quitte la connexion, mais en général il s'agit plutôt d'un envoi de données "quit" qui fait que le serveur coupe la connexion (timeout et déconnexions sauvages à part forcément).
    Mindiell
    "Souvent, femme barrit" - Elephant man

  8. #8
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut
    @Mat007 :
    Je viens de retenter la référence, mon souci c'est que je n'arrive pas à déclarer mes classes :
    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
    class Telnet
    {
    public :
        //Constructeur par défaut
        Telnet(void);
        //Connexion à un site
        void Connect(const std::string& host, unsigned short port = 23);
        //Pour vérifier qu'on est bien connecté
        bool IsConnected(void);
        //Fonction chargée de récupérer les données envoyées par le serveur
        friend void fReceptionThread(void* UserData);
     
    private :
        class ReceptionThread : public sf::Thread
        {
        public:
            enum TelState
            {
                state_data,
                state_command,
                state_option
            };
            void SetReference (Telnet &telnet) { myTelnet = telnet; }
        private:
            virtual void Run();
     
            Telnet myTelnet;
        };
     
        //Les informations de connexion
        std::string     mHostName;
        sf::IPAddress   mHost;
        unsigned short  mPort;
        //La socket permettant de recevoir et d'envoyer des données
        sf::SocketTCP   mConnection;
        //Booléen permettant de savoir si on est toujours connecté ou pas
        bool            mIsConnected;
        ReceptionThread mReceptionThread;
    };
    Avec ce code, mon souci c'est que le type du membre "Telnet" de ma classe ReceptionThread est incomplet. Comment cette classe peut connaitre la classe Telnet en cours de spécification ?
    Si je sépare la classe et la place en dessous :
    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
    //Pur que Telnet connaisse ReceptionThread
    class ReceptionThread;
     
    class Telnet
    {
    public :
        //Constructeur par défaut
        Telnet(void);
        //Connexion à un site
        void Connect(const std::string& host, unsigned short port = 23);
        //Pour vérifier qu'on est bien connecté
        bool IsConnected(void);
        //Fonction chargée de récupérer les données envoyées par le serveur
        friend void fReceptionThread(void* UserData);
     
    private :
        //Les informations de connexion
        std::string     mHostName;
        sf::IPAddress   mHost;
        unsigned short  mPort;
        //La socket permettant de recevoir et d'envoyer des données
        sf::SocketTCP   mConnection;
        //Booléen permettant de savoir si on est toujours connecté ou pas
        bool            mIsConnected;
        ReceptionThread mReceptionThread;
    };
     
        class ReceptionThread : public sf::Thread
        {
        public:
            enum TelState
            {
                state_data,
                state_command,
                state_option
            };
            void SetReference (Telnet &telnet) { myTelnet = telnet; }
        private:
            virtual void Run();
     
            Telnet myTelnet;
        };
    Là c'est mReceptionThread qui est de type incomplet !

    Au secours !
    Mindiell
    "Souvent, femme barrit" - Elephant man

  9. #9
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut
    Au final, Laurent Gomila (le concepteur de la SFML) m'a indiqué l'utilisation possible d'un héritage privé qui semble réaliser toutes mes demandes et en plus simplifie mon code !

    Merci à tous, je continue sur cette voie !
    Mindiell
    "Souvent, femme barrit" - Elephant man

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 524
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 524
    Points : 29 972
    Points
    29 972
    Par défaut
    J'ai peut être été un peu vite dans mes explications, et c'est pourquoi tu ne comprend sans doute pas ce que je voulais dire...

    Conceptuellement parlant, tu peux envisager le fait que les protocoles FTP, HTTP ou Telnet ne soient... que des protocoles, c'est à dire, des conventions qui déterminent la manière dont deux oridnateurs communiquent entre eux.

    On remarque généralement qu'une communication se fait en plusieurs étapes:
    1. la connexion et l'identification ("salut, moi c'est l'ordinateur à koala" et la réponse"salut, je suis le serveur DVP")
    2. l'envoi de requête "simple" ("envoie moi la page machin","dit moi ce qui se trouve dans le dossier bidule",ou quoi ou qu'est-ce) ou "complexe" ("veux tu bien gérer telle chose (un formulaire ) " )
    3. la réponse à une requête ("bien sur: voici le résultat") (retour éventuel en 2 )
    4. la déconnexion ("bon, je fais comme l'infanterie : je me tire ailleurs" réponse "A ciao bon soir")
    La "composition" et la "forme" des différentes requêtes (y compris celle de connexion et de déconnexion) et réponses sont déterminées par le protocole (on "cause" différemment à un serveur telnet qu'à un serveur HTTP), mais, l'un dans l'autre, on peut estimer qu'une requête (FTP, HTTP ou Telnet) est... une requête, et qu'une réponse (quel que soit le protocole) est... une réponse, la seule différence résidant dans le protocole utilisé pour créer la requête ou pour interpréter la réponse.

    D'un autre coté, tu peux te dire que tu aura chaque fois une "connexion".

    Là aussi, le type de connexion est déterminé par le protocole utilisé, mais, de manière générale, elle mettra en communication l'ordinateur "local" et un "serveur distant", tous deux étant définis par leurs nom, adresses IP et masques sous réseaux éventuels.

    Mais, qu'elle soit de type FTP, HTTP ou Telnet n'aura une importance... que de manière strictement interne à la connexion, et tu pourrais très bien envisager de vouloir créer une collection (un tableau ) de connexions dans laquelle tu trouverais deux connexions FTP, trois connexions HTTP et une connexion Telnet à autant de serveurs différents.

    Tu les utiliserais, pour faire simple, en leur demandant un certain nombre de choses qui devront s'adapter au protocole utilisé:
    • se (re) connecter
    • envoyer une requête
    • récupérer une réponse
    • se déconnecter

    En outre, tu peux te dire qu'il y aura sans doute plusieurs requêtes émises et autant de réponses récupérées, les requêtes devant être effectuées dans un ordre particulier, et les réponses devant être gérées... dans l'ordre de leur arrivée.

    Tu devrais donc avoir, pour chaque connexion active, une file d'attente de requêtes à envoyer et... une file d'attente de réponses à gérer.

    Les requêtes à envoyer et les réponses obtenues seraient dépendantes du protocole utilisé, et donc créées (respectivement gérées) en fonction du protocole utilisé par la connexion.

    Enfin, tu auras l'aspect "multi threadé" de ton application qui permettra de... gérer chaque connexion dans un thread séparé.

    Mais nous ne sommes, à l'extrême limite, même pas obligés de savoir quel type de connexion est utilisé par un thread: tout ce qu'il faut, c'est que le thread soit, en interne, en mesure
    1. de recréer la connexion si elle a été perdue
    2. de savoir quand la requête a été correctement envoyée
    3. de savoir quand la connexion a reçu la réponse complête
    Autrement dit, si plusieurs protocoles doivent être gérés, tu aurais les hiérarchies de classes suivantes:
    • Protocole, dont dérivent les protocoles
      • FTP,
      • HTTP et
      • Telnet,
      • ...

      chacun réimplémentant la manière de construire une requête et d'interpréter la réponse
    • Connection, dont dérivent les
      • ConnexionFTP,
      • ConnectionHTTP et
      • ConnexionTelnet,
      • ...
      qui pourraient simplement utiliser l'interface de Protocole pour travailler
    • Request dont dérivent les requêtes particulières aux différents protocoles utilisant chacune une structure de données capable de contenir les informations à transmettre au travers de la connexion ad hoc compatible avec le protocole utilisé telles que
      • RequestFTP,
      • RequestHTTP et
      • RequestTelnet
      • ...
    • Response dont dérivent dont dérivent les réponses particulières aux différents protocoles utilisant chacune une structure de données capable de contenir les informations récupérées après l'envoi d'une requête telles que
      • ResponseFTP,
      • ResponseHTTP ou
      • ResponseTelnet
      • ...
    • RequestQueue contenant la file d'attente des différentes requêtes à envoyer et dont on veillera à les remplir avec des requêtes correspondant à la connexion qui les gérera
    • ResponsQueue contenant la file d'attente des réponses en attente de gestion et dont on veillera à les remplir avec des réponses correspondant à la connexion qui les fournis (nous pourrions envisager d'avoir un type particulier de ResponseQueue par protocole )
    • ConnexionThread qui peut parfaitement ne pas savoir de quelle connexion il s'agit réellement, et qui se contente, chaque fois qu'il reçoit un signal "fin de réponse", de lancer l'émission de la requête suivante s'il y en une
    En présentant les choses sous cette forme, je me rend effectivement compte que le code tel que je le proposais ne correspond pas (j'étais parti sur l'idée que tout partait du thread )

    Mais cette manière de voir les choses te permettra une énorme souplesse par la suite, surtout si tu t'intéresse un peu aux template, car il pourrait suffire de définir un nouveau protocole et quelques alias de types (pour les connexions, requêtes et réponses) pour ajouter le protocole manquant

    Maintenant, tu trouvera peut être des classes de base correspondant à Protocole, Request ou Response dans SFML
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut
    Bon,

    J'avais donc bien compris le principe. Le plus gros problème que je vois c'est que, justement, Telnet n'est pas un protocole tout à fait comme les autres : on peut recevoir des retours à tout moment et il ne faut pas bloquer le client. C'est selon l'application de l'autre côté. En FTP ou HTTP, on demande quelque chose et on attend la réponse. En Telnet, on ne fait qu'envoyer les touches tapées sur le clavier et certaines provoqueront, ou pas, des réactions de la part du serveur. D'où la nécessité d'utiliser un Thread pour "écouter" ce qui arrive en permanence.

    En tout cas, merci de ton aide, c'est toujours très instructif
    Mindiell
    "Souvent, femme barrit" - Elephant man

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    octobre 2004
    Messages
    11 524
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 524
    Points : 29 972
    Points
    29 972
    Par défaut
    Citation Envoyé par Mindiell Voir le message
    Bon,

    J'avais donc bien compris le principe. Le plus gros problème que je vois c'est que, justement, Telnet n'est pas un protocole tout à fait comme les autres : on peut recevoir des retours à tout moment et il ne faut pas bloquer le client. C'est selon l'application de l'autre côté. En FTP ou HTTP, on demande quelque chose et on attend la réponse. En Telnet, on ne fait qu'envoyer les touches tapées sur le clavier et certaines provoqueront, ou pas, des réactions de la part du serveur. D'où la nécessité d'utiliser un Thread pour "écouter" ce qui arrive en permanence.

    En tout cas, merci de ton aide, c'est toujours très instructif
    Il n'y a, a priori, aucun problème avec cela, pour autant que tu adapte la "définition" de ce qu'est une "requête" au protocole utilisé...

    Si, selon le protocole Telnet, une requête est "simplement" un caractère dont un certain nombres ont une signification particulière, grand bien lui fasse...

    La seule différence qu'il peut y avoir avec les autres protocoles, c'est que, plutôt que de récupérer la réponse du serveur sous la forme du retour de la fonction d'envoi, tu fais la distinction claire entre ce qui est émis d'un coté et ce qui est reçu de l'autre, avec sans doute, un thread dédié à l'émission et un autre dédié à la réception

    Mais cela, ce ne sont en définitive, que des détails d'implémentation propre au protocole envisagé
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #13
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2006
    Messages : 735
    Points : 544
    Points
    544
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Il n'y a, a priori, aucun problème avec cela, pour autant que tu adapte la "définition" de ce qu'est une "requête" au protocole utilisé...

    Mais cela, ce ne sont en définitive, que des détails d'implémentation propre au protocole envisagé
    Justement !
    Le protocole Telnet ne spécifie pas explicitement cela. C'est plutôt l'application derrière qui va gérer cela comme elle le souhaite avec les bonnes options Telnet. Les autres protocoles (http et ftp par exemple) n'ont pas ce besoin de distinction.
    Mais bon, de toute facon, je n'ia besoin que de Telnet, et Laurent a déjà fait Ftp et Http, donc je vais pas en faire plus que nécessaire
    Mindiell
    "Souvent, femme barrit" - Elephant man

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

Discussions similaires

  1. [Python 3.X] Ma class ne fonctionne pas dans un Thread
    Par Bydouil dans le forum GUI
    Réponses: 7
    Dernier message: 19/08/2014, 20h25
  2. [debutant] Les classes amies
    Par kiroukou dans le forum Débuter
    Réponses: 14
    Dernier message: 04/02/2005, 14h50
  3. Erreur : La classe ne gère pas Automation..
    Par BrunoM45 dans le forum VBA Access
    Réponses: 1
    Dernier message: 09/09/2004, 11h24
  4. Réponses: 4
    Dernier message: 15/01/2004, 23h53
  5. Les classes amies en Delphi
    Par Bruno75 dans le forum Langage
    Réponses: 3
    Dernier message: 02/09/2003, 20h34

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