Partage
  • Partager sur Facebook
  • Partager sur Twitter

ReadyRead Qt

Sujet résolu
    21 avril 2022 à 23:51:15

    Bonjour/bonsoir !

    Je souhaite lire des QByteArray à travers un socket. La connexion marche parfaitement.

    Voici la fonction que je propose côté cliente :

        QDataStream in(m_socket);
        in.setByteOrder(QDataStream::LittleEndian);
    
        short int commandIndex { -1 };
        quint32 payloadSize;
    
        while(m_socket->bytesAvailable()) {
            in.startTransaction();
    
            QByteArray const magicBytes { in.device()->read(4) };
            QByteArray const command { in.device()->read(12) };
    
            in >> payloadSize;
    
            QByteArray payload;
            payload = in.device()->readAll();
    
            commandIndex = getCommandIndex(m_commands, command);
    
            if (!in.commitTransaction()) return;
    
            qDebug() << "MSG = " << payload.toHex();
    
            if(!isValidNetworkVersion(magicBytes) || commandIndex == -1)
            {
                logPrint("INVALID => " + payload.toHex());
                break;
            }
    
            parse(commandIndex, payload);
        }
    Cependant, mes messages sont mal reçus :/. Lorsque j'envoie simultanément du côté serveur deux messages distinct, le slot readyRead est appelé qu'une seul fois. Mais parse est appelé une fois seulement. Je récupère le size de mon message que après que j'ai reçu minimum 24 bytes. Comment pourrai-je régler ce problème de lecture ? Je souhaite aussi pouvoir recevoir un grand nombre de donnée en Mo.

    Bien cordialement.
    • Partager sur Facebook
    • Partager sur Twitter
      22 avril 2022 à 10:21:13

      >Lorsque j'envoie simultanément

      Multithread ?

      Si oui, votre code est-il multi-thread proof ?

      • Partager sur Facebook
      • Partager sur Twitter
      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
        22 avril 2022 à 13:13:04

        non le serveur envoie juste deux messages en même temps comme ceci :

        m_socket->write(QByteArray::fromHex("0b11090776657273696f6e00000000003e0000007fadf3c8a0090100fa8c6262000000002c2f5361746f7368693a302e362e3828546865206a6f75726e6579206973206e6576657220656e64696e67292f5136000001"));
        m_socket->write(QByteArray::fromHex("0b11090776657261636b000000000000000000005df6e0e2"));

        et le client reçoit en un seul message :

        0b11090776657273696f6e00000000003e0000007fadf3c8a0090100fa8c6262000000002c2f5361746f7368693a302e362e3828546865206a6f75726e6579206973206e6576657220656e64696e67292f51360000010b11090776657261636b000000000000000000005df6e0e2

        Il me résulte ceci du débug :

        2022-04-22T13:11:22Z MSG => c4e826ada0090100588d6262000000002c2f5361746f7368693a302e362e3828546865206a6f75726e6579206973206e6576657220656e64696e67292f51360000010b11090776657261636b000000000000000000005df6e0e2
        2022-04-22T13:11:22Z CAN PARSE...
        2022-04-22T13:11:22Z Header error: Invalid message type (invalid checksum for peer=a.x.y.z)

        Le checksum ne correspond évidemment pas. Normalement le client doit lire les deux messages séparément.

        J'ai mis un peu à jour le code :

        QDataStream in(m_socket);
            in.setByteOrder(QDataStream::LittleEndian);
        
            do
            {
                in.startTransaction();
        
                if(in.device()->size() < NETWORK_HEADER_SIZE)
                    return;
        
                QByteArray const magicBytes { in.device()->read(4) };
                QByteArray const command { in.device()->read(12) };
        
                quint32 payloadSize;
                in >> payloadSize;
        
                QByteArray const checksum { in.device()->read(4) };
                QByteArray payload;
        
                short int const commandIndex { getCommandIndex(m_commands, command) };
        
                if(!isValidNetworkVersion(magicBytes) || commandIndex == -1)
                {
                    logPrint("payload=" + payload.toHex());
                    return;
                }
        
                /** Append payload data payloadSize */
                payload.append(in.device()->read(payloadSize));
        
                /** Print the payload hex data of the command. */
                //logPrint("MSG => " + payload.toHex());
        
                if (!in.commitTransaction()) return;
        
                //logPrint("CAN PARSE...");
        
                parse(commandIndex, checksum, payload);
            } while(m_socket->bytesAvailable());


         
        Cordialement.



        -
        Edité par Loopite 22 avril 2022 à 13:47:25

        • Partager sur Facebook
        • Partager sur Twitter
          22 avril 2022 à 13:55:22

          QDataStream ???

          C'est donc un protocole orienté flux, pas message, comme TCP.

          En TCP, il n'y pas/plus de message. C'est un flux, donc concaténation du contenu de chaque appel en sortie.

          Si votre protocole est orienté message, utilisez UDP, ça sera plus simple.

          • Partager sur Facebook
          • Partager sur Twitter
          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
            22 avril 2022 à 14:06:23

            Non, TCP garantie l'envoie et l'intégrité de données. UDP c'est une véritable catastophe à ce sujet.
            Mais là n'est pas la question. Comment je pourrai lire le flux avec une concaténation du contenu correctement selon vous ?

            Loopite.
            • Partager sur Facebook
            • Partager sur Twitter
              22 avril 2022 à 16:54:31

              Est-ce tu lis correctement tes données ?

              D'après ton 2nd code (sauf erreur), ton message se compose, dans l'ordre, de:

              • 4 octets "magicbytes"
              • 12 octets command
              • 4 octets payloadSize
              • 4 octets de checksum
              • payloadsize octets qui constitue ton payload
              A noter que dans ton if ligne 22-26, payload est vide.
              il faudrait afficher/vérifier pour chacune des lectures si tu lis bien ce que tu envois (on voit que les 1ères données ne sont pas les mêmes entre l'affichage de MSG et les données envoyées; les 1ères données de MSG correspondant au bloc du checksum)
              • Partager sur Facebook
              • Partager sur Twitter
                22 avril 2022 à 17:01:50

                Bonjour,

                La structure est en effet :
                • 4 octets "magicbytes"
                • 12 octets command
                • 4 octets payloadSize
                • 4 octets de checksum
                • payloadsize octets qui constitue ton payload

                Après modifications et conseils, j'ai écrit ceci :

                void TCPClientController::dataReceived()
                {
                    bool header { false };
                    qint32 payloadSize { 0 };
                    short int commandIndex { -1 };
                
                    QByteArray checksum, payload;
                
                    QDataStream in(m_socket);
                    in.setByteOrder(QDataStream::LittleEndian);
                
                    for(;;)
                    {
                        if(!header)
                        {
                            if(in.device()->size() < NETWORK_HEADER_SIZE)
                                return;
                
                            QByteArray const magicBytes { in.device()->read(4) };
                            QByteArray const command { in.device()->read(12) };
                            in >> payloadSize;
                            checksum = in.device()->read(4);
                
                            if(payloadSize > NETWORK_MAX_SIZE)
                            {
                                logPrint("Header error: Payload too big, bits=" + QString::number(payloadSize) + ", peer=" + m_hostAddr);
                                break;
                            }
                
                            commandIndex = getCommandIndex(m_commands, command);
                
                            if(!isValidNetworkVersion(magicBytes) || commandIndex == -1)
                                break;
                
                            payload = in.device()->read(payloadSize);
                        }
                
                        if(payload.size() < payloadSize)
                        {
                            m_socket->waitForReadyRead();
                            payload.append(in.device()->read(payloadSize - payload.size()));
                        }
                
                        if(payload.size() == payloadSize && in.device()->size() == 0)
                        {
                            parse(commandIndex, checksum, payload);
                            break;
                        }
                
                        /** More than 1 requests ... */
                        parse(commandIndex, checksum, payload.mid(0, payloadSize));
                        payload.remove(0, payloadSize);
                        header = false;
                    }
                }

                Ce qui permet de filtrer correctement tous mes messages types reçus. La fonction marche correctement. Auriez-vous d'autres recommandations , trucs que je n'aurais pas vu ou si le code a une "faille" ?

                Bien cordialement.

                -
                Edité par Loopite 22 avril 2022 à 19:09:55

                • Partager sur Facebook
                • Partager sur Twitter
                  22 avril 2022 à 18:31:27

                  bacelar a écrit:

                  >Lorsque j'envoie simultanément

                  Multithread ?

                  Si oui, votre code est-il multi-thread proof ?

                  Qt est un peu particulier. Le réseau est multi thread en interne, mais la partie public utilise les events et signaux-slots, pour avoir de la programmation événementielle, sans avoir besoin de thread (on peut threader si on a des traitements complexes a faire, mais ce n'est pas une obligation). C'est l'event loop qui se charge de gérer les tâches "asynchrones" (qui sont en fait séquentielles en pratique).

                  C'est une "feature" de Qt pour justement éviter d'avoir a manipuler des threads et donc simplifier le code.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    22 avril 2022 à 19:37:01

                    Reste que TCP est orienté flux, pas message.

                    Si vous "envoyez" 2 "messages" depuis le client, le serveur recevra probablement les 2 messages concaténés, parce que les algorithmes anti-congestion de TCP, ou autres, auront trouvés que tout envoyer en 1 coup était plus pertinant.

                    IL N'Y A PAS DE MESSAGE EN TCP, C'EST UN FLUX D’OCTETS.

                    • Partager sur Facebook
                    • Partager sur Twitter
                    Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                      22 avril 2022 à 19:51:34

                      Oui, bien sur. D'ailleurs, de mémoire, les codes d'exemple de Qt montre comment les messages sont découpés manuellement en passant la taille dans les données, pour pouvoir découper chaque message à la réception. (Et je crois que justement, pour les codes d'exemple de QUdpSocket, ce découpage n'est pas fait dans les code d'exemple)

                      -
                      Edité par gbdivers 22 avril 2022 à 19:52:17

                      • Partager sur Facebook
                      • Partager sur Twitter
                        22 avril 2022 à 19:52:25

                        bacelar a écrit:

                        Reste que TCP est orienté flux, pas message.

                        Si vous "envoyez" 2 "messages" depuis le client, le serveur recevra probablement les 2 messages concaténés, parce que les algorithmes anti-congestion de TCP, ou autres, auront trouvés que tout envoyer en 1 coup était plus pertinant.

                        IL N'Y A PAS DE MESSAGE EN TCP, C'EST UN FLUX D’OCTETS.


                        Oui, très certainement, mais c'est ce que la fonction fait. Elle parse le flux reçu via l'utilisation du QDataStream pour lire les bytes beaucoup plus facilement qu'avec un vieux m_socket->readAll();

                        -
                        Edité par Loopite 22 avril 2022 à 19:53:56

                        • Partager sur Facebook
                        • Partager sur Twitter
                          22 avril 2022 à 19:53:56

                          "ReadAll" n'est pas vieux. C'est un outil comme un autre et qui marche très bien pour certains besoins.
                          • Partager sur Facebook
                          • Partager sur Twitter
                            22 avril 2022 à 19:55:10

                            gbdivers a écrit:

                            "ReadAll" n'est pas vieux. C'est un outil comme un autre et qui marche très bien pour certains besoins.


                            Oui, mais ce que je voulais dire c'est que l'utilisation du QDataStream dans mon cas est plus utile que le readAll().
                            • Partager sur Facebook
                            • Partager sur Twitter
                              22 avril 2022 à 20:02:02

                              Je sais pas trop. L'intérêt d'un stream, c'est de pouvoir traiter les données en même temps qu'elles arrivent, pour ne pas être obligé d'attendre d'avoir tout reçu pour les traiter.

                              A partir du moment où tu attend d'avoir tout reçu pour traiter, un readAll pourrait peut être convenir sans problème. Ou un UDP. Tout dépend de tes besoins et il y a toujours plusieurs façons (plus ou moins complexes) de faire la meme chose.

                              EDIT : surtout qu'au final, tu utilises aussi bien le data stream que des fonctions de lecture directe avec read(). 

                              -
                              Edité par gbdivers 22 avril 2022 à 20:05:22

                              • Partager sur Facebook
                              • Partager sur Twitter
                                22 avril 2022 à 20:08:13

                                Très bien. Je vais voir ceci de mon côté. En tout cas, merci pour votre aide et vos conseils :lol: !
                                Je ferme le sujet.

                                Bien respectueusement.
                                • Partager sur Facebook
                                • Partager sur Twitter

                                ReadyRead Qt

                                × 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