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 :

Client TCP/IP : paquets reçus incomplets [Débuter]


Sujet :

Réseau

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2014
    Messages : 7
    Points : 6
    Points
    6
    Par défaut Client TCP/IP : paquets reçus incomplets
    Bonjour,
    Je rencontre actuellement un petit problème dans mon programme. J'ai crée un client tcp/ip me permettant de recevoir les données que m'envoi un serveur distant, celui-ci peut etre configuré pour, soit envoyer en continu des données, soit envoyer une donnée bien particulière en fonction d'une commande qu'on lui aura envoyer via le client. Mon client reçoit bien les données mais le problème et que la réception se fait en sous-paquets. Au lieu de recevoir par exemple : 2 16 58 1 1 1 1 1 0
    je recois :2 1
    6 58
    1 1 1 1 1
    0
    Comment faire pour pallier ce problème.
    Cordialement

  2. #2
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Août 2008
    Messages
    26 621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2008
    Messages : 26 621
    Points : 188 609
    Points
    188 609
    Par défaut


    Le problème est très probablement que tu récupères les données de manière synchrone (sans signaux ni slots, dans la terminologie Qt) : dès que tu reçois un paquet TCP, tu commences à traiter. Deux solutions : soit tu récupères les données petit à petit et tu les concatènes avant de les traiter, soit tu ne les récupères qu'une fois le paquet de données au complet (même problème que http://www.developpez.net/forums/d14...e/#post8019646). Ça, c'est une explication générale : montre un peu de code pour avoir des détails plus adaptés .
    Vous souhaitez participer aux rubriques Qt (tutoriels, FAQ, traductions) ou HPC ? Contactez-moi par MP.

    Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2014
    Messages : 7
    Points : 6
    Points
    6
    Par défaut
    bonjour, donc voila mon code, les 2 slots qui nous interesse sont "on_boutonEnvoyer_clicked" et "donneesRecues", merci pour ton temps

    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
    #include "FenClient.h"
    #include "ui_FenClient.h"
    #include <QTimer>
    #include <QDateTime>
    FenClient::FenClient()
    {
        setupUi(this);
        QTimer *timer=new QTimer(this);
        connect(timer ,SIGNAL(timeout()),this,SLOT(afficherTemps()));
        timer->start();
     
        socket = new QTcpSocket(this);
        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)));
        tailleMessage = 0;
        if(SIGNAL (connecte()))
        {
            message->setStyleSheet("background : white");
        }
        else if(SIGNAL(deconnecte()))
        {
            message->setStyleSheet("background : white");
        }
    }
     
     
     
     
    void FenClient::afficherTemps()
    {
     
        QDateTime dateTime=QDateTime::currentDateTime();
        QString dateTime_text=dateTime.toString("       Le dd.MM.yyyy    à hh:mm:ss");
        dateHeure->setText(dateTime_text);
     
    }
     
    // Tentative de connexion au serveur
    void FenClient::on_boutonConnexion_clicked()
    {
        // On annonce sur la fenêtre qu'on est en train de se connecter
        listeMessages->append(tr("<em>Tentative de connexion en cours...</em>"));
        boutonConnexion->setEnabled(false);
        socket->abort(); // On désactive les connexions précédentes s'il y en a
        socket->connectToHost(serveurIP->text(), serveurPort->value()); // On se connecte au serveur demandé
    }
    // Envoi d'un message au serveur
    void FenClient::on_boutonEnvoyer_clicked()
    {
        QByteArray paquet;
           QDataStream out(&paquet, QIODevice::WriteOnly);
     
           // On prépare le paquet à envoyer
           QString messageAEnvoyer =  Label->text() ;
     
           out << (quint16) 0;
           out << messageAEnvoyer;
           out.device()->seek(0);
           out << (quint16) (paquet.size() - sizeof(quint16));
     
           socket->write(paquet); // On envoie le paquet
     
           message->clear(); // On vide la zone d'écriture du message
           message->setFocus(); // Et on remet le curseur à l'intérieur
     
    }
    // Appuyer sur la touche Entrée a le même effet que cliquer sur le bouton "Envoyer"
    void FenClient::on_message_returnPressed()
    {
        on_boutonEnvoyer_clicked();
    }
    // On a reçu un paquet (ou un sous-paquet)
    void FenClient::donneesRecues()
    {
     
     
        QByteArray message;
        if (socket->bytesAvailable() < 0)
        return;
        message = socket->readAll();
        qDebug() << QString::fromUtf8("Des données ont été reçues en provenance du serveur");
        qDebug() << QString::fromUtf8("Octets reçus : ") << message.size();
        qDebug() << QString::fromUtf8("Message reçu du serveur : ") << message;
        listeMessages->append(message);
    }
     
     
     
     
    // Ce slot est appelé lorsque la connexion au serveur a réussi
    void FenClient::connecte()
    {
        listeMessages->append(tr("<em>Connexion réussie !</em>"));
        boutonConnexion->setEnabled(true);
     
    }
    // Ce slot est appelé lorsqu'on est déconnecté du serveur
    void FenClient::deconnecte()
    {
        listeMessages->append(tr("<em>Déconnecté du serveur</em>"));
     
    }
    // Ce slot est appelé lorsqu'il y a une erreur
    void FenClient::erreurSocket(QAbstractSocket::SocketError erreur)
    {
        switch(erreur) // On affiche un message différent selon l'erreur qu'on nous indique
        {
            case QAbstractSocket::HostNotFoundError:
                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:
                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:
                listeMessages->append(tr("<em>ERREUR : le serveur a coupé la connexion.</em>"));
                break;
            default:
                listeMessages->append(tr("<em>ERREUR : ") + socket->errorString() + tr("</em>"));
        }
        boutonConnexion->setEnabled(true);
    }

  4. #4
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Août 2008
    Messages
    26 621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2008
    Messages : 26 621
    Points : 188 609
    Points
    188 609
    Par défaut
    Tu ne pourras pas avoir du réseau l'information de paquet complet, elle n'y existe simplement pas. HTTP propose deux mécanismes : indiquer la longueur totale du paquet dans un champ de l'en-tête HTTP (tu récupères donc des données tant que ce champ n'est pas lu, puis tu sais exactement ce à quoi t'attendre) ou bien le fameux chunked encoding (lire des données jusqu'à un signal qui indique la fin). Une troisième solution est possible : utiliser des données structurées, type XML, où tu peux lire des données tant que le contenu n'est pas valide.

    Le plus simple reste d'ajouter un premier champ à chaque message, qui contient la longueur de ce qui suit (par exemple, des chiffres décimaux, puis un séparateur type \n, chaque petit paquet commençant par sa taille) ; côté récepteur, tu lis les données jusqu'au séparateur, tu analyses le début comme la taille du reste, puis tu récupères des données jusqu'à avoir tout (avec plusieurs appels de donneesRecues()).
    Vous souhaitez participer aux rubriques Qt (tutoriels, FAQ, traductions) ou HPC ? Contactez-moi par MP.

    Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2014
    Messages : 7
    Points : 6
    Points
    6
    Par défaut
    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
    void FenClient::on_boutonEnvoyer_clicked()
    {
        QByteArray paquet=0;
        QDataStream out(&paquet, QIODevice::WriteOnly);
        // On prépare le paquet à envoyer
     
        QString messageAEnvoyer = Label->text();
        out << (quint16) 0;
        out << messageAEnvoyer;
        out.device()->seek(0);
        out << (quint16) (paquet.size() - sizeof(quint16));
        socket->write(Label->text().toUtf8() + "\r\n"); // ou +"\r\n"
       // socket->write(paquet); // On envoie le paquet
        message->clear(); // On vide la zone d'écriture du message
        message->setFocus(); // Et on remet le curseur à l'intérieur
     
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void FenClient::donneesRecues()
        {
            while(socket->canReadLine())
            {
                QByteArray message = socket->readLine().trimmed();
                listeMessages->append(QString::fromUtf8(message));
            }
        }
    apres avoir fouillé un peu, voila mon nouveau code modifier, je reçois bien les données de mon serveur en une seule fois, le problème maintenant et que je reçois correctement les trames se terminant par un \n mais les autres n'arrive pas de suite, je dois cliquer plusieurs fois pour qu'elles s'affichent.

  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,

    C'est normal dans la mesure où vous utilisez canReadLine(). Récapitulons un peu le fonctionnement de votre programme de sorte que vous compreniez ce que vous devez faire.

    Dans le serveur, vous avez une méthode permettant d'envoyer des données :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    QString messageAEnvoyer = Label->text();
    out << (quint16) 0;
    out << messageAEnvoyer;
    out.device()->seek(0);
    out << (quint16) (paquet.size() - sizeof(quint16));
    Avant de faire quoi que ce soit, il vous faut déjà comprendre ce que ce code fait. Tout d'abord, il insère dans le paquet à envoyer 2 octets (le quint16) en y mettant 0 dedans. À la suite de cela, il ajoute messageAEnvoyer, puis il revient au début du paquet pour y remplacer les deux octets mis à 0 par la taille du paquet en octets moins 2 octets, par exemple 20. Ainsi, chaque paquet que vous envoyez aura au minimum deux octets dedans, contenant la taille effective du paquet, suivi du message. Pour la récupération, on peut donc faire ainsi :

    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
    // Avec quint16 _packetSize; dans les privates de la classe
     
    FenClient::FenClient()
    {
        // ...
        // On indique dans le constructeur que l'on n'a pas encore reçu de paquet
        _packetSize = 0;
    }
     
    void FenClient::donneesRecues()
    {
        // Si packetSize est à 0, c'est qu'un nouveau paquet vient d'arriver
        if (_packetSize == 0)
        {
            // On en lit les 2 premiers octets uniquement s'il y en a bien au moins 2 de disponibles, afin de récupérer la taille du message
            if (socket->bytesAvailable() >= sizeof(quint16))
                socket >> _packetSize;
        }
     
        // Si packetSize est supérieur à 0, c'est qu'on a déjà récupéré la taille du message : s'il y a assez de données à lire, c'est que le paquet est entièrement arrivé
        if (_packetSize > 0 && socket->bytesAvailable() >= _packetSize)
        {
            // Vu que le paquet est entièrement arrivé, on le récupère en entier
            QString message(socket->read(_packetSize));
            // On en fait ce qu'on veut, par exemple l'ajouter dans la liste
            listeMessages->append(message);
            // On réinitialise bien packetSize de sorte d'indiquer que l'on attend un nouveau paquet
            _packetSize = 0;
        }
    }
    N'hésitez pas si vous avez des questions,

    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
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2014
    Messages : 7
    Points : 6
    Points
    6
    Par défaut
    Bonjour,

    Pour la ligne ci-dessous j'obtiens ce message d'erreur : invalid operands of types 'QTcpSocket*' and 'quint16 {aka short unsigned int}' to binary 'operator>>'
    socket >> _packetSize;
    ^



  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,

    Effectivement, j'ai pas fait attention à ce point, c'était du code écrit à la volée sur le forum :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if (socket->bytesAvailable() >= sizeof(quint16))
        socket >> _packetSize;
    =>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if (socket->bytesAvailable() >= sizeof(quint16))
    {
        QByteArray packetSizeData = socket->read(sizeof(quint16)); // Lecture des deux octets
        QDataStream stream(packetSizeData); // Création d'un QDataStream
        stream >> _packetSize; // Utilisation de l'opérateur >> pour transformer les deux octets lus en un quint16, entier de deux octets
    }
    En théorie, cela devrait fonctionner avec ça.

    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
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2014
    Messages : 7
    Points : 6
    Points
    6
    Par défaut ecriture dans un fichier xls
    bonjour,

    Merci pour votre aide

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

Discussions similaires

  1. [Réseau] Problème Serveur Client TCP linux embarqué
    Par FabienpERRIN dans le forum Réseau
    Réponses: 2
    Dernier message: 31/07/2007, 20h47
  2. Probleme Tache Serveur et Client TCP Linux Embarque
    Par FabienpERRIN dans le forum Réseau
    Réponses: 1
    Dernier message: 30/07/2007, 11h53
  3. TCP : Utilisation paquet par paquet plutot que flux
    Par dockurt2k dans le forum Développement
    Réponses: 6
    Dernier message: 12/01/2007, 11h03
  4. [Delphi 2006 .NET] Client TCP
    Par Griswold dans le forum Delphi .NET
    Réponses: 1
    Dernier message: 06/06/2006, 23h32
  5. Client Tcp/ Serveur TCP
    Par Phébus dans le forum Web & réseau
    Réponses: 2
    Dernier message: 21/07/2005, 16h35

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