Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème réseau avec Qt (C++)

Données du QDataStream

    11 juillet 2019 à 22:33:10

    Bonjour,

    Désolé d'avance si le sujet a déjà été fait mais il ne me semble pas avoir trouvé. Je n'ai pas l'habitude de demander de l'aide, je finis toujours par trouver mais là je ne vois vraiment pas.


    Donc, j'ai suivi le cours "Programmer avec le langage C++" et en étant à la dernière partie avec Qt, le réseau, je rencontre un problème. J'ai écrit exactement le même code que dans le cours, tout semble fonctionner et pourtant quand je lance le serveur, puis mon client (ou mes clients), l'envoi d'un message ne fonctionne pas.

    Ayant retracé d'où pouvait venir le problème, j'ai remarqué que le QByteArray était bien rempli d'un Quint16 et de mon QString, la taille variant bien en fonction de la taille de la chaine de caractère. Aussi côté serveur, le QDataStream qui lit la socket reçoit bien tous les octets envoyés et son statut montre qu'il n'est pas défaillant. Pourtant, quand je stocke les informations du QDataStream dans Quint16 tailleMessage puis QString message, ils sont respectivement rempli par 0 et une chaine vide (je suppose vu que rien ne s'affiche).

    void FenServeur::donneesRecues()
    {
        // 1 : on reçoit un paquet (ou un sous-paquet) d'un des clients
    
        // On détermine quel client envoie le message (recherche du QTcpSocket du client)
        QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
        if (socket == 0) // Si par hasard on n'a pas trouvé le client à l'origine du signal, on arrête la méthode
            return;
    
        // Si tout va bien, on continue : on récupère le message
        QDataStream in(socket);
    
        if (tailleMessage == 0) // Si on ne connaît pas encore la taille du message, on essaie de la récupérer
        {
            if (socket->bytesAvailable() < int(sizeof(quint16))) // On n'a pas reçu la taille du message en entier
                 return;
    
            in >> tailleMessage; // Si on a reçu la taille du message en entier, on la récupère
        }
    
        // Si on connaît la taille du message, on vérifie si on a reçu le message en entier
        if (socket->bytesAvailable() < tailleMessage) // Si on n'a pas encore tout reçu, on arrête la méthode
            return;
    
        // Si ces lignes s'exécutent, c'est qu'on a reçu tout le message : on peut le récupérer !
        QString message;
        in >> message;
    
        // 2 : on renvoie le message à tous les clients
        envoyerATous(message);
    
        // 3 : remise de la taille du message à 0 pour permettre la réception des futurs messages
        tailleMessage = 0;
    }
    void FenClient::on_boutonEnvoyer_clicked()
    {
        QByteArray paquet;
        QDataStream out(&paquet, QIODevice::WriteOnly);
    
        // On prépare le paquet à envoyer
        QString messageAEnvoyer = tr("<strong>") + pseudo->text() +tr("</strong> : ") + message->text();
    
        out << quint16(0);
        out << messageAEnvoyer;
        out.device()->seek(0);
        out << (quint16(paquet.size()) - quint16(sizeof(quint16)));
    
        std::cout<<"octets ecrits : "<<socket->write(paquet)<<std::endl; // On envoie le paquet
    
        message->clear(); // On vide la zone d'écriture du message
        message->setFocus(); // Et on remet le curseur à l'intérieur
    }

    Ci-dessus les seuls morceaux de code dont vous devriez avoir besoin pour comprendre ce dont je parle (bien qu'accessible ici https://openclassrooms.com/fr/courses/1894236-programmez-avec-le-langage-c/1902751-communiquez-en-reseau-avec-son-programme).

    Je tiens également à préciser que j'ai fait le test avec le code fourni par le prof (bien que tout à fait identique normalement) et même résultat. Aussi, comme je fais le test en local ça ne devrait pas importer, mais mon port est bien ouvert sur mon réseau.


    Si vous avez des idées d'où vient le problème, ou besoin de plus d'informations, faites-moi signe.

    Merci d'avance.

    • Partager sur Facebook
    • Partager sur Twitter
      12 juillet 2019 à 11:35:26

      Est tu sur que tes client soient bien connectés, sait tu si les messages arrivent au serveur?

      Sinon regardes la class QDebug, ça peut aider à localiser exactement le problème (en particulier la fonction qDebug()<<QString;)

      Chez moi le programme du prof fonctionne sans problèmes!

      • Partager sur Facebook
      • Partager sur Twitter
        12 juillet 2019 à 12:09:01

        Note : c'est pas le code d'un prof mais le cours de ce site, qui est a éviter. Il enseigne un C++ retraité, et c'est la même chose sur Qt, avec certain code proposé qui ne marchent plus. 

        Pour les problèmes qui contient : https://informaticienzero.github.io/c++-avec-openclassrooms-ou-comment-perdre-son-temps/

        Pour les conseils de cours : https://openclassrooms.com/forum/sujet/apprendre-le-c-23

        • Partager sur Facebook
        • Partager sur Twitter
          12 juillet 2019 à 13:46:34

          Tout d'abord merci de vos réponses.

          @PieWar : Oui mes clients sont bien connectés, à coup de std::cout j'ai vérifié que le serveur recevait bien le paquet comme je l'avais expliqué, il reçoit bien le paquet de la bonne taille donc c'est que le client est connecté. Je vais voir comment fonctionne QDebug, j'essaie ça et vous tiens au courant.

          @K4kugen : Merci pour les liens, je les consulterais pour apprendre à mieux programmer. Actuellement je suis à l'université et j'ai bien l'impression qu'on nous apprend comme tu dis un C++ retraité, les cours sont totalement vieillissant. En fait j'ai uniquement suivi les chapitres de Qt du cours d'OC, je voulais apprendre à l'utiliser car il facilite l'implémentation du réseau mais bien sûr c'est la seule partie que je n'arrive pas à faire fonctionner.

          EDIT :
          @PieWar : J'ai bien peur de ne pas comprendre comment fonctionne, ni comment me servir du QDebug... Si tu peux m'éclairer.

          -
          Edité par CharlyBollinger 12 juillet 2019 à 14:19:58

          • Partager sur Facebook
          • Partager sur Twitter
            12 juillet 2019 à 15:12:24

            Si tu suis uniquement les partie Qt, tu ne devrais pas devoir désapprendre beaucoup de chose, mais fait quand même attention, comme dit plus haut, certains codes proposés ne compilent même pas. Si tu es à l'aise avec l'anglais, Qt propose ses propres tutos : https://doc.qt.io/qt-5/gettingstarted.html

            Pour QDebug, pense bien aux parenthèses (et l'include) :

                qDebug() << "Date:" << QDate::currentDate();
                qDebug() << "Types:" << QString("String") << QChar('x') << QRect(0, 10, 50, 40);
                qDebug() << "Custom coordinate type:" << coordinate;


            (Exemple tiré de la doc : https://doc.qt.io/qt-5/qdebug.html)

            Il permet d'afficher des variables/strings dans la console (pour une utilisation simple), mais un std::cout fait l'affaire (pour cette utilisation simpliste)

            -
            Edité par K4kugen 12 juillet 2019 à 15:17:08

            • Partager sur Facebook
            • Partager sur Twitter
              13 juillet 2019 à 7:59:20

              La fonction qDegub(), qui s'utilise comme ceci: qDebug()<<QString;  sert a aficher du texte dans la partie 'sortie de l’application' de QT, alors que l'appli est en cours d’exécution. Cela peut servir a voir si une fonction s'est lancée, etc... 

              • Partager sur Facebook
              • Partager sur Twitter
                13 juillet 2019 à 11:30:12

                D'accord donc ça fait exactement comme ce que j'avais fait avec des std::cout, le message est vide comme je disais dans le sujet mais quand je vérifie le nombre d'octet côté serveur avec socket->bytesAvailable() j'ai bien le bon nombre d'octets qui sont disponible. La question est pourquoi il ne me les range pas dans mon QString (et mon Quint16), ou alors si il le fait bien pourquoi les données semblent vides.
                • Partager sur Facebook
                • Partager sur Twitter
                  13 juillet 2019 à 12:03:14

                  Quand tu dis:

                  "Pourtant, quand je stocke les informations du QDataStream dans Quint16 tailleMessage puis QString message, ils sont respectivement rempli par 0 et une chaine vide (je suppose vu que rien ne s'affiche)."

                  , tu essai de stocker ca a quel niveau? Coté serveur ou Client?

                  • Partager sur Facebook
                  • Partager sur Twitter
                    13 juillet 2019 à 13:55:16

                    Côté serveur bien sûr. Car le client envoie bien le paquet, en effet si côté serveur je reçois bytesAvailable avec la valeur de la taille en octet du paquet envoyé côté client, cela veut dire que le paquet est réçu côté serveur. Or quand je stocke ces données dans un Quint16 pour la taille puis un QString pour le reste du message j'obtiens les valeurs 0 et "".


                    (Là du coup j'essaie de refaire totalement autrement de mes propres moyens avec la doc Qt, mais bon je ne comprends quand même pas ce qui n'allait pas avec le code dont on parle)

                    • Partager sur Facebook
                    • Partager sur Twitter
                      15 juillet 2019 à 23:19:10

                      Le soucis que tu as, vient probablement du fait que lorsque tu vas lire la taille, le client a probablement déjà commencé à envoyer la chaîne de caractères, du coup BytesAvailiable renvoie une valeur supérieure à ce que tu attendais et tu te retrouves dans la panade (TCP/IP te garantis que les paquets arriveront dans l'ordre où ils ont été émis, pas qu'ils n'auront pas été scindés ou regroupés...). Rien ne te garantit que ce qui se trouve dans un socket est une trame complète, rien ne garantit non plus, qu'il n'y a pas un bout de la trame suivante. La programmation réseau met à mal notre perception du temps, a cause de ça, c'est peut être la programmation la plus difficile qui soit.

                      Lorsqu'on fait de la communication réseau, on travaille avec un protocole. Un protocole simple (et relativement efficace) est de toujours commencer une communication par un "magic number", c'est facile à mettre en oeuvre, tu lis les 4 premiers octets, tu cast en int et si tu ne retrouve pas le "magic number", tu déconnectes. Derrière, tu passes la taille de ce que tu vas envoyer, puis généralement un checksum (classiquement un crc32), le magic number te permet de vérifier que tu es bien synchro, la taille de ce qui suit te permet de savoir où se finit la trame courante et le checksum que tu as bien reçu ce qui t'as été envoyé. Là tu peux faire face à la plupart des pannes. Pour faire face aux malveillances, tu passes au chiffrement, c'est un peu plus compliqué dans la mise en oeuvre, mais pas dans le principe.

                      -
                      Edité par int21h 16 juillet 2019 à 0:39:36

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug

                      Problème réseau avec Qt (C++)

                      × 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