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 Discussion :

QLocalSocket : problème de mémoire ?


Sujet :

Réseau

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut QLocalSocket : problème de mémoire ?
    Bonjour à tous,

    Depuis maintenant quelques jours (pour ne pas dire semaines), je me trouve confronté à un problème que je n’arrive pas à résoudre.
    En fait, je cherche à développer un programme qui permet de sauvegarder des données qui me sont transmises en UDP. Etant donné que la fréquence d’émission est très importante et des caractéristiques de l’UDP, le cahier des charges m'impose de « découper » mon programme en 2 Threads :
    - Le programme principal qui récupère les données en UDP, les met dans un buffer et écrit ces mêmes données dans un QLocalSocket (pipe)
    - Un QThread, moins prioritaire que le programme principal et qui tourne en parallèle, qui récupère les données sur le QLocalSocket. Pour la lecture, j’utilise la méthode readLine().

    Afin d’éviter les accès concurrents, j’ai mis en place un système de QMutex.

    Le problème, c’est que de manière aléatoire (au bout de 2 secondes comme au bout de 10 minutes), je me retrouve avec les erreurs :
    - ASSERT failure in QVector<T>::remove: "index out of range".
    - QObject::killTimer: timers cannot be stopped from another thread
    Sachant que les deux erreurs vont de paire.

    Si je ne me trompe pas, la première erreur signifie que j’essaie de supprimer un espace mémoire que je n’ai pas alloué. Et le seul objet qui est susceptible d’utiliser un QVector et qui supprime quelque, c’est le QLocalSocket (lors de la lecture).
    Je ne comprends pas forcément la deuxième erreur étant donné que je n'utilise pas de timer ... Je suppose qu'un Timer est associé à la classe QThread. Mais sachant qu'il y a les QMutex, je suppose qu'un QThread ne peut pas arrêter un autre.
    Et le debugger de Qt ne me donne aucune information sur la ligne (ou l’objet) qui pose problème.

    Voici le constructeur du QThread :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Thread::Thread(QMutex *p_pipeMutex)
    {
        if(NULL != p_pipeMutex)
        {
            m_p_pipeMutex = p_pipeMutex;
        }
    }
    Voici le code de la lecture des données dans le QLocalSocket :
    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
    void Thread::run()
    {    
        while (1)
        {
            if (avoirClient == true) // Si QLocalSocket est connecté à un QLocalServer
            {
                m_p_pipeMutex->lock();
                QByteArray reception;
                if ((nouveauClient->canReadLine())) // nouveauClient est un QLocalSocket. canReadLine vérifie qu'il y a un caractère '\n'. Les données transmises par trame contiennent toujours ce caractère.
                {
                    if (nouveauClient->bytesAvailable() > 2048) // Je lis le QLocalSocket seulement si il est rempli un minimum.
                    {
                        reception = nouveauClient->readLine();
                        compteur = compteur + 1;
                        std::cout << "Reception Thread " << compteur << " : " << reception.data() << std::endl;
                    }
                }
                m_p_pipeMutex->unlock();
                usleep(500); // Juste histoire de ne pas avoir un affichage qui saccade.
            }
    Voici le code de la création du QThread :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        thread = new Thread(&m_pipeMutex);
        thread->start(QThread::LowPriority);
    Voici le code de l'envoi :
    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
    while (udpSocketTrace->hasPendingDatagrams())
        {
            bufferSimple = new QByteArray;
            Structure temp;
            QHostAddress sender;
            quint16 senderPort;
            bufferSimple->resize(udpSocketTrace->pendingDatagramSize()); // Resize de la variable qui va récupérer les données
            udpSocketTrace->readDatagram(bufferSimple->data(), bufferSimple->size(),&sender, &senderPort); // Récupération des données UDP
            memcpy(temp.texte, bufferSimple->data(), 1024); // Donnée récupérée mise dans une structure
            memcpy(&bufferTournant[indice], &temp, sizeof(Structure)); // Cette même structure mise dans un buffer
     
            m_pipeMutex.lock();
            pipe->write(bufferTournant[indice].texte); // Ecriture dans le QLocalSocket
            m_pipeMutex.unlock();
            bufferSimple->clear();
     
            indice = indice + 1;
     
            if (indice == 60000)
            {
                indice = 0;
            }
        }
    Donc je me tourne vers vous pour savoir si vous avez déja été confronté à ce type de problème ou si vous avez une solution (ou une idée).
    Merci d'avance pour votre réponse.

    Titounet

  2. #2
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonjour,

    En général, et vous l'avez compris, un index out of range sous entend que pour un QVector de 10 éléments, vous tentez par exemple d'accéder au 32e. De là, j'imagine que ça doit être un problème de concurrence qui écrase le size du QVector ou quelque chose dans le genre, malgré votre mutex. Pouvez-vous nous montrer plus de code, cette fois-ci autour de la transmission de "nouveauClient" et de "avoirClient" du côté du thread ? En fait, tous les éléments de communication entre le thread présenté et le thread principal seraient les bienvenus.

    Bonne soirée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  3. #3
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Bonjour à tous,

    Comme demandé, voici le programme. Pour des raisons de lisibilité, j'ai retiré tous les affichages console.

    Voici le programme de l'IHM (programme principal) :

    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
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    #include "InterfaceGraphique.h"
    #include "ui_InterfaceGraphique.h"
     
    InterfaceGraphique::InterfaceGraphique(QWidget *parent) :  QWidget(parent), ui(new Ui::InterfaceGraphique)
    {
        ui->setupUi(this);
     
        indice = 0;
     
        thread = new Thread(&m_pipeMutex); // Création du Thread
        thread->start(QThread::NormalPriority);
     
        bufferTournant = NULL;
        bufferTournant =(Structure *) malloc (60000 * sizeof(Structure));
     
        pipe = new QLocalSocket(this); Création du pipe
        pipe->connectToServer("PIPE"); // Connexion au "Serveur " pipe. Comme le "Serveur" pipe est créé dans le constructeur du Thread et que le Thread est créé dès le départ, aucun problème de non connexion
     
        // Toutes les connexions dont j'ai besoin
        socket = new QTcpSocket(this);
        udpSocketCommande = new QUdpSocket(this);
        udpSocketTrace = new QUdpSocket(this);
     
        connect(ui->boutonConnexion, SIGNAL(clicked()), this, SLOT(on_boutonConnexion_clicked()));
        connect(ui->boutonEnvoyer, SIGNAL(clicked()), this, SLOT(on_boutonEnvoyer_clicked()));
        connect(ui->boutonQuitter, SIGNAL(clicked()), qApp, SLOT(quit()));
        connect(socket, SIGNAL(readyRead()), this, SLOT(donneesRecues()));
        connect(socket, SIGNAL(connected()), this, SLOT(connecte()));
        connect(socket, SIGNAL(disconnected()), this, SLOT(deconnecte()));
        connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(erreurSocket(QAbstractSocket::SocketError)));
        connect(udpSocketTrace, SIGNAL(readyRead()), this, SLOT(readPendingDatagram()));
        connect(thread, SIGNAL(receptionThread(QByteArray)), this, SLOT(affichage(QByteArray)));
        connect(ui->commande, SIGNAL(currentIndexChanged(int)), this, SLOT(updateCommande(int)));
        connect(ui->boutonCurseur, SIGNAL(clicked()), this, SLOT(mettreCurseurFin()));
     
        udpSocketCommande-> bind(QHostAddress::Any, ui->serveurPortUdpCommande->value());
     
        tailleMessage = 0;
    }
     
    InterfaceGraphique::~InterfaceGraphique()
    {
        delete ui;
        thread->exit(0);
    }
     
    void InterfaceGraphique::mettreCurseurFin()
    {
        ui->listeMessages->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
        ui->listeMessages->setFocus();
    }
     
    void InterfaceGraphique::updateCommande(int index)
    {
        if (index == 0)
        {
            ui->parametre1->setEnabled(true);
            ui->parametre2->setEnabled(true);
            ui->parametre3->setEnabled(true);
        }
        else if (index == 1)
        {
            ui->parametre1->setEnabled(false);
            ui->parametre2->setEnabled(false);
            ui->parametre3->setEnabled(false);
        }
        else if (index == 2)
        {
            ui->parametre1->setEnabled(false);
            ui->parametre2->setEnabled(false);
            ui->parametre3->setEnabled(false);
        }
        else if (index == 3)
        {
            ui->parametre1->setEnabled(true);
            ui->parametre2->setEnabled(false);
            ui->parametre3->setEnabled(false);
        }
        else if (index == 4)
        {
            ui->parametre1->setEnabled(false);
            ui->parametre2->setEnabled(true);
            ui->parametre3->setEnabled(false);
        }
        else if (index == 5)
        {
            ui->parametre1->setEnabled(false);
            ui->parametre2->setEnabled(false);
            ui->parametre3->setEnabled(true);
        }
    }
     
    // Affichage sur fenetre
    void InterfaceGraphique::affichage(QByteArray octets)
    {
        ui->listeMessages->append(octets.data());
    }
     
    // Action effectuée à chaque fois que la socket UDP Trace reçoit quelque chose du serveur
    void InterfaceGraphique::readPendingDatagram()
    {
        while (udpSocketTrace->hasPendingDatagrams())
        {
            bufferSimple = new QByteArray;
            Structure temp;
            QHostAddress sender;
            quint16 senderPort;
            bufferSimple->resize(udpSocketTrace->pendingDatagramSize());
            udpSocketTrace->readDatagram(bufferSimple->data(), bufferSimple->size(),&sender, &senderPort);
            memcpy(temp.texte, bufferSimple->data(), 1024);
            memcpy(&bufferTournant[indice], &temp, sizeof(Structure));
     
            m_pipeMutex.lock();
            pipe->write(bufferTournant[indice].texte);
            m_pipeMutex.unlock();
            bufferSimple->clear();
     
            indice = indice + 1;
     
            if (indice == 60000)
            {
                indice = 0;
            }
        }
    }
     
    // Tentative de connexion au serveur
    void InterfaceGraphique::on_boutonConnexion_clicked()
    {
        // On annonce sur la fenêtre qu'on est en train de se connecter
        ui->listeMessages->append(tr("<em>Tentative de connexion en cours...</em>"));
        ui->boutonConnexion->setEnabled(false);
        socket->abort(); // On désactive les connexions précédentes s'il y en a
        socket->connectToHost(ui->serveurIP->text(), ui->serveurPortTcp->value()); // On se connecte au serveur demandé
        udpSocketTrace-> bind(QHostAddress::Any, ui->serveurPortUdpTrace->value());
    }
     
    // Envoi d'un message au serveur
    void InterfaceGraphique::on_boutonEnvoyer_clicked()
    {
        QString message = "*/";
        QString separateurEspace = " ";
        if (ui->commande->currentIndex() == 0)
        {
            message = message + ui->commande->currentText() + separateurEspace + ui->parametre1->text() + separateurEspace + ui->parametre2->currentText() + separateurEspace + ui->parametre3->currentText() ;
        }
        else if (ui->commande->currentIndex() == 1)
        {
            message = message + ui->commande->currentText()+ separateurEspace;
        }
        else if (ui->commande->currentIndex() == 2)
        {
            message = message + ui->commande->currentText()+ separateurEspace;
        }
        else if (ui->commande->currentIndex() == 3)
        {
            message = message + ui->commande->currentText() + separateurEspace + ui->parametre1->text()+ separateurEspace;
        }
        else if (ui->commande->currentIndex() == 4)
        {
            message = message + ui->commande->currentText() + separateurEspace + ui->parametre2->currentText();
        }
        else if (ui->commande->currentIndex() == 5)
        {
            message = message + ui->commande->currentText() + separateurEspace + ui->parametre3->currentText();
        }
        udpSocketCommande->writeDatagram(message.toStdString().c_str(),message.size(), QHostAddress(ui->serveurIP->text()), ui->serveurPortUdpCommande->value());
    }
     
    // Appuyer sur la touche Entrée a le même effet que cliquer sur le bouton "Envoyer"
    void InterfaceGraphique::on_message_returnPressed()
    {
        on_boutonEnvoyer_clicked();
    }
     
    // On a reçu un paquet (ou un sous-paquet)
    void InterfaceGraphique::donneesRecues()
    {
        /* Même principe que lorsque le serveur reçoit un paquet :
        On essaie de récupérer la taille du message
        Une fois qu'on l'a, on attend d'avoir reçu le message entier (en se
        basant sur la taille annoncée tailleMessage)
        */
        QDataStream in(socket);
        if (tailleMessage == 0)
        {
            if (socket->bytesAvailable() < (int)sizeof(quint16))
                return;
            in >> tailleMessage;
        }
        if (socket->bytesAvailable() < tailleMessage)
            return;
        // Si on arrive jusqu'à cette ligne, on peut récupérer le message entier
        QString messageRecu;
        in >> messageRecu;
        // On affiche le message sur la zone de Chat
        ui->listeMessages->append(messageRecu);
        // On remet la taille du message à 0 pour pouvoir recevoir de futurs messages
        tailleMessage = 0;
    }
     
    // Ce slot est appelé lorsque la connexion au serveur a réussi
    void InterfaceGraphique::connecte()
    {
        ui->listeMessages->append(tr("<em>Connexion réussie !</em>"));
        ui->boutonConnexion->setEnabled(true);
    }
     
    // Ce slot est appelé lorsqu'on est déconnecté du serveur
    void InterfaceGraphique::deconnecte()
    {
        ui->listeMessages->append(tr("<em>Déconnecté du serveur</em>"));
    }
     
    // Ce slot est appelé lorsqu'il y a une erreur
    void InterfaceGraphique::erreurSocket(QAbstractSocket::SocketError erreur)
    {
        switch(erreur) // On affiche un message différent selon l'erreur qu'on nous indique
        {
            case QAbstractSocket::HostNotFoundError:
                ui->listeMessages->append(tr("<em>ERREUR : le serveur n'a pas pu être trouvé. Vérifiez l'IP et le port.</em>"));
                break;
            case QAbstractSocket::ConnectionRefusedError:
                ui->listeMessages->append(tr("<em>ERREUR : le serveur a refusé la connexion. Vérifiez si le programme \"serveur\" a bien été lancé. Vérifiez aussi l'IP et le port.</em>"));
                break;
            case QAbstractSocket::RemoteHostClosedError:
                ui->listeMessages->append(tr("<em>ERREUR : le serveur a coupé la connexion.</em>"));
                break;
            default:
                ui->listeMessages->append(tr("<em>ERREUR : ") + socket->errorString() + tr("</em>"));
        }
        ui->boutonConnexion->setEnabled(true);
    }
    Voici le programme du Thread :

    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
    Thread::Thread(QMutex *p_pipeMutex)
    {
        avoirClient = false;
        pipeServer = new QLocalServer(this); // Création du "Serveur" pipe
        pipeServer->listen("PIPE");
        connect(pipeServer, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
        if(NULL != p_pipeMutex)
        {
            m_p_pipeMutex = p_pipeMutex; // Initiatlisation du Mutex
        }
        compteur = 0;
    }
     
    void Thread::run()
    {    
        while (1) // run() tourne en continu
        {
            if (avoirClient == true) // Si un client est connecté
            {
                m_p_pipeMutex->lock();
                QByteArray reception;
                if ((nouveauClient->canReadLine()))
                {
                    if (nouveauClient->bytesAvailable() > 2048)
                    {
                        reception = nouveauClient->readLine();
                        emit receptionThread(reception); // Signal qui déclenche l'affichage du coté du programme principal. Il n'a aucune incidence sur le pipe.
                        compteur = compteur + 1;
                    }
                }
                m_p_pipeMutex->unlock();
                usleep(5);
            }
        }
    }
     
    void Thread::nouvelleConnexion()
    {
        nouveauClient = pipeServer->nextPendingConnection(); //Définition du pipe
        nouveauClient->flush();
        nouveauClient->open(QIODevice::ReadWrite);
        clientsPipe << nouveauClient; // Je créé une liste de clients au cas où plusieurs clients se connectent. Pour le moment, il n'y en a toujours qu'un seul.
        avoirClient = true;
    }
    Si quelqu'un a une idée, n'hésitez pas !!

    Merci d'avance pour votre réponse.

    Titounet

  4. #4
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonjour,

    Que se passe-t-il si vous changez :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    connect(thread, SIGNAL(receptionThread(QByteArray)), this, SLOT(affichage(QByteArray)));
    Par ceci ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    connect(thread, SIGNAL(receptionThread(QByteArray)), this, SLOT(affichage(QByteArray)), Qt::QueuedConnection);
    Le signal receptionThread() est-il bien écrit ainsi et non avec des références ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    signals:
        void receptionThread(QByteArray data);
    Un détail pouvant être lié à votre crash si la fonction est extrêmement fréquemment appelée (en théorie, ça n'en arrivera pas à ce point) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bufferSimple = new QByteArray;
    Va entraîner une fuite de mémoire. N'hésitez pas à passer par un allocation statique dans le cas actuel :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QByteArray bufferSimple;
    Autre point : nouvelleConnexion() sera toujours appelé dans le contexte du main thread et non du thread que vous avez créé. Par conséquent, il pourrait être judicieux de le mutexer.

    Voilà pour les premiers éléments.

    Bonne journée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  5. #5
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Citation Envoyé par Amnell Voir le message
    Bonjour,

    Que se passe-t-il si vous changez :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    connect(thread, SIGNAL(receptionThread(QByteArray)), this, SLOT(affichage(QByteArray)));
    Par ceci ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    connect(thread, SIGNAL(receptionThread(QByteArray)), this, SLOT(affichage(QByteArray)), Qt::QueuedConnection);
    Bah je vous avoue que cela ne change pas grand chose sur le fonctionnement.
    Même en fouillant le debugger, je ne vois pas de différences notables ...

    En ce qui concerne l'allocation statique, en fait, j'avais commencé par l'utiliser. Puis, pour une raison encore inconnue, j'ai décidé de changer ...
    Je viens de repasser en statique et là non plus, pas de différence majeure.

    Autre point : nouvelleConnexion() sera toujours appelé dans le contexte du main thread et non du thread que vous avez créé. Par conséquent, il pourrait être judicieux de le mutexer.
    Par contre, je n'ai pas très bien compris ce que vous avez dit sur le slot nouvelleConnexion().
    Ce slot est associé au Thread et non au programme principal. Dans le Thread, dès qu'un client vient se connecter, le slot nouvelleConnexion() s'éxecute. Mais il ne sert qu'à l'initialisation du pipe.
    Et ce slot n'est n'appelé qu'une seule fois (lors de la connexion du client, étant donné qu'il n'y a qu'un client).
    Donc je ne vois pas forcément l'intérêt de le mutexer ...

    Je continue néanmoins ma batterie de tests sur ces différents points, en attendant d'autres idées ...

    Titounet

  6. #6
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonsoir,

    En théorie, vu que ce n'est appelé qu'une fois, nouvelleConnection() ne devrait pas poser de problème. Par contre, est-il possible de voir également la déclaration de "Structure" ?

    Un point que je constate, c'est que, dans readPendingDatagram(), votre bufferTournant et votre temp ne sont jamais initialisés, par conséquent vous ne pouvez pas vous attendre à ce qu'il y ait un zéro à la fin des données copiées avec les memcpy, ce qui pourrait perturber le pipe->write(), vu que celui-ci va chercher le zéro à la fin du bytearray pour trouver la fin de bufferTournant[indice].texte et ainsi pour en déduire la taille (Valgrind sortirait sans doute un invalid read sur ce point). De là, je ne sais pas si ça pourrait être lié au problème qui se pose réellement.

    Bonne soirée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  7. #7
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Bonjour à tous,

    En fait, les données qui me sont transmises font toutes 1024 octets, '\n' compris. Donc dans la méthode readDatagram(), je lis 1024 octets à chaque fois et je reçois les données cohérentes.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     bufferSimple->resize(udpSocketTrace->pendingDatagramSize()); // = 1024
    udpSocketTrace->readDatagram(bufferSimple->data(), bufferSimple->size(),&sender, &senderPort);
    // Lecture de 1024 octets par 1024 octets

    En ce qui concerne la Structure, il s'agit d'une simple chaine de 1024 caractères.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
            typedef struct
            {
                char texte[1024];
            }Structure;
    Par contre, effectivement, je vais voir coté initialisation si cela donne quelque chose dès que j'aurai le bon PC de disponible.

  8. #8
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonjour,

    Dans ce cas, n'oubliez pas de remplacer :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pipe->write(bufferTournant[indice].texte);
    Par :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pipe->write(bufferTournant[indice].texte, 1024);
    Le cas échéant, il ira très probablement lire plus loin. En voyant votre structure, un memset à 0 me semble également nécessaire.

    Bonne journée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  9. #9
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Bonjour,

    J'ai testé toutes les solutions que tu as proposées mais aucune n'a l'effet escompté malheureusement

    Au vu des différentes solutions proposées, j'ai de plus en plus l'impression que le soucis provient de la lecture dans le QLocalSocket (méthode readLine), et peut être même des QMutex qui ne font peut être pas ce que je voudrais.

    Cependant, vous n'êtes pas le premier à qui je parle de ce problème qui me parle de Valgrind.
    Y a t-il un équivalent sur Windows ?

    Titounet

  10. #10
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonsoir,

    Dans ce cas, je ne sais pas trop, à vrai dire. Après, je me pose une question : à quelles fins voulez-vous utiliser QLocalSocket ? Pourquoi ne pas juste mettre les messages reçus en Queue du thread pour que celui-ci les traite ? Cela retirerait une partie de la charge de calculs. Je peux illustrer mes propos avec du code si cela vous intéresse.

    En ce qui concerne Valgrind, un équivalent que j'ai utilisé sur Windows est Dr. Memory (https://code.google.com/p/drmemory/, avec par exemple la version sans installation : https://bintray.com/bruening/DrMemor...-portable/view). Je n'ai cependant pas réussi à l'intégrer à Qt Creator, je ne fais par conséquent que le lancer manuellement. Pour cela, il faut se mettre dans une console, faire un cd vers le dossier bin de la version de Qt utilisée pour être à l'emplacement des fichiers .dll, et lancer depuis cet emplacement votre application. Cela prend toujours bien plus de temps que sans drmemory à s'exécuter :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    cd C:\Qt\5.3\5.3\mingw482_32\bin
    C:\...\DrMemory-Windows-1.8.0-8\bin\drmemory.exe C:\...\build-ans_titre-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/sans_titre.exe
    Compilez en debug pour avoir les lignes de vos fichiers sources dans le rapport. Si vous ne comprenez pas le rapport d'erreur, n'hésitez pas à le poster de sorte que l'on puisse se pencher dessus et voir ce qui pose problème.

    Bonne soirée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  11. #11
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Bonjour,

    L'utilisation du pipe fait partie de mon cahier des charges. Le seul pipe que je connais en Qt, c'estt le QLocalSocket. Je ne suis pas sûr qu'il en existe d'autres.
    Je veux bien un exemple si cela ne vous dérange pas.

    Je viens d'installer Dr Memory mais l'application "a cessé de fonctionner" ...
    Alors qu'à partir de QtCreator, il n'y a aucun problème ...

    Et sur la console, un warning apparait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ~~Dr.M~~ Dr. Memory version 1.8.0
    ~~Dr.M~~ Running "C:\Users\Desktop\UDP\build-Client-Desktop_Qt_5_3_MinG
    W_32bit-Debug\debug\Client.exe"
    WARNING: unable to locate results file since can't open C:\DrMemory-Windows-1.8.
    0-8\drmemory\logs/resfile.1820: 2
    There was likely an early crash, perhaps due to interference from invasive secur
    ity software.  Try disabling other software.
    ~~Dr.M~~ WARNING: application exited with abnormal code 0xc0000005
    DrMemory-Windows-1.8.0-8\drmemory\logs/resfile.1820: 2. Le dossier DrMemory-Client.exe.1820.000 mais il est vide.


    Titounet

  12. #12
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonjour,

    Apparemment, l'application DrMemory se situe dans un répertoire en lecture seule, ce qui l'empêche d'enregistrer les logs. Le fait de donner les droits d'écriture sur le dossier, d'exécuter drmemory en tant qu'administrateur ou encore uniquement de changer l'emplacement de DrMemory devrait régler le problème du premier warning. Concernant le second warning indiquant un crash, c'est très probablement lié au fait que votre application ne trouve pas les fichiers .dll nécessaires à son exécution (par exemple Qt5Core.dll). C'est pour cette raison que je disais de bien faire un cd vers le dossier bin de la version de Qt (C:\Qt\5.3\mingw482_32\bin chez moi, par exemple).

    Est-ce bien ce que vous avez fait ?

    Bonne journée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  13. #13
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Re,

    En complément de ma réponse précédente, voici pour l'exemple demandé (j'y ai intégré au passage la fermeture en douceur du thread de sorte que l'application se termine avec le code 0 et non un gros crash).

    main.cpp :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
     
        MainWindow w;
        w.show();
     
        return a.exec();
    }
    mainwindow.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
    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
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
     
    #include <QtWidgets>
    #include <QThread>
    #include <QMutex>
     
    class MyThread : public QThread
    {
        Q_OBJECT
     
    public:
        MyThread(QMutex &mutex)
            : _mutex(mutex),
              _hasToStop(false) // Permet d'arrêter proprement le thread
        {
        }
     
        // Méthode d'ajout de données dans le buffer (équivalent du write dans le pipe), appelée dans le contexte du main thread
        void addPendingData(const QByteArray &data)
        {
            _mutex.lock();
            _buffer.append(data); // Simple ajout des données dans le buffer
            _mutex.unlock();
        }
     
        // Méthode appelée dans le contexte du main thread permettant de fermer proprement le thread
        void stopTread()
        {
            _hasToStop = true;
        }
     
    signals:
        void dataAvailable(QString data);
     
    protected:
        virtual void run()
        {
            while (!_hasToStop) // Tant que le thread est en marche
            {
                _mutex.lock(); // Lock du mutex vu qu'on va manipuler le buffer
                if (_buffer.size() > 50) // Simulation de votre condition sur la taille du buffer
                {
                    QBuffer buffer; // Création d'un QBuffer qui fournit les méthodes canReadLine() et readLine() que ne possède pas QByteArray
                    buffer.setData(_buffer); // On lui insère les données du _buffer dedans (ce qui fait qu'il contient une "copie" du QByteArray _buffer, voir (1))
                    buffer.open(QIODevice::ReadOnly); // Ouverture du QBuffer en lecture
                    if (buffer.canReadLine()) // Si on peut lire une ligne
                    {
                        QByteArray data = buffer.readLine(); // Lecture de la ligne
                        buffer.close(); // Fermeture du buffer vu qu'on n'en a plus besoin
                        buffer.setData(QByteArray()); // Vidage du buffer (2)
                        _buffer.remove(0, data.size()); // Retrait de la ligne lue du _buffer (sans cela, _buffer ne serait pas vidé progressivement et accumulerait les données)
                        emit dataAvailable(data); // emit des données lues
                    }
                }
                _mutex.unlock();
                sleep(1);
            }
            deleteLater(); // À la sortie du while, cela signifie que le thread a fini son exécution : libération de la mémoire
        }
     
    private:
        QMutex &_mutex;
        QByteArray _buffer;
        bool _hasToStop;
    };
     
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
     
    public:
        MainWindow(QWidget *parent = 0)
            : QMainWindow(parent)
        {
            QPushButton *btn = new QPushButton("Cliquez ici !");
            setCentralWidget(btn);
            connect(btn, SIGNAL(clicked()), this, SLOT(sendData()));
     
            _thread = new MyThread(_mutex);
            _thread->start();
     
            connect(_thread, SIGNAL(dataAvailable(QString)), this, SLOT(displayData(QString)), Qt::QueuedConnection);
        }
     
        virtual ~MainWindow()
        {
            _thread->stopTread();
            _thread->wait();
        }
     
    public slots:
        void displayData(QString data)
        {
            qDebug() << "Received:" << data;
        }
     
        void sendData()
        {
            static int current = 0;
            _thread->addPendingData(QString("Bonjour, ceci est le test %1\n").arg(current).toLatin1()); // Simulation d'envoi de données (3)
            qDebug() << "Sent:" << current;
            ++current;
        }
     
    private:
        QMutex _mutex;
        MyThread *_thread;
    };
     
    #endif // MAINWINDOW_H
    Commentaires détaillés associés :

    (1) Le QBuffer, via setData(), va copier le QByteArray dans son QByteArray interne. Cependant, vu que QByteArray utilise l'implicit sharing, cela sera négligeable, en terme des coûts (http://qt-project.org/doc/qt-5/implicit-sharing.html - cet article pourrait vous intéresser, expliquant pourquoi faire QByteArray array1 = array2 a un coût quasi-nul en terme de performances, tout comme QList<int> list1 = list2, peu importe la taille de l'un ou le nombre de l'élément de l'autre).
    (2) Justement par rapport à l'implicit sharing, il est nécessaire de vider complètement le QBuffer, sinon, une modification du _buffer mènera le buffer interne du QBuffer à être copié réellement, vu qu'ils ne partageront alors plus les mêmes données.
    (3) Vous constaterez qu'en cliquant beaucoup sur le bouton, cela va mettre du texte en attente dans le thread, ce qui le mènera à lire progressivement et à afficher progressivement les logs de lecture. Cette partie est à remplacer par l'utilisation de vos sockets.

    De là, avec les optimisations concernant l'implicit sharing, je suis peut-être un peu trop dans le détail, mais si vous avez une masse considérable de données qui passent, cela sera sans doute non négligeable. En tous cas, je pense que cette solution est bien plus performante qu'un pipe, mais si vous êtes obligé d'en utiliser, alors elle ne servira pas à grand chose.

    N'hésitez pas si vous avez des questions,

    Bonne journée,
    Louis
    N'oubliez pas de consulter la FAQ Qt ainsi que les cours et tutoriels C++/Qt !

    Dernier article : Débuter avec les Enlightenment Foundation Libraries (EFL)
    Dernières traductions : Introduction à Qt Quick - Applications modernes avec Qt et QML
    Vous cherchez un livre sur Qt 5, Qt Quick et QML ? Créer des applications avec Qt 5 - Les essentiels

  14. #14
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2012
    Messages
    28
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mars 2012
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Bonjour,

    J'ai bien compris le procédé.
    En fait, c'est exactement le même fonctionnement qu'un pipe sauf que vous effectuez chacune des étapes manuellement au lieu d'utiliser une classe toute faite (QLocalSocket).

    J'ai réussi à l'appliquer à mon problème et ca fonctionne !!!
    Reste à savoir si le principe sera accepté par les autres.

    Par contre, je suis maintenant persuadé que le problème venait du QLocalSocket. Il doit y avoir une propriété qui a dû m'échapper ...

    En tout cas, un grand merci !

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

Discussions similaires

  1. [WORD]Problème de mémoire
    Par Dnx dans le forum VBA Word
    Réponses: 17
    Dernier message: 05/10/2005, 14h48
  2. [Tomcat][Spring] Problème utilisation mémoire
    Par Wutintin dans le forum Hibernate
    Réponses: 12
    Dernier message: 08/09/2005, 14h57
  3. [Crystal Report]Problème de mémoire avec le moteur RDC
    Par sur_uix dans le forum SAP Crystal Reports
    Réponses: 3
    Dernier message: 26/05/2005, 09h09
  4. Problème de mémoire avec BDE
    Par Machuet dans le forum Bases de données
    Réponses: 3
    Dernier message: 13/07/2004, 10h11
  5. Problème de mémoire Affichage images
    Par Repti dans le forum C++Builder
    Réponses: 6
    Dernier message: 29/03/2004, 20h06

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