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 :

Récupération des données en TCP incomplète


Sujet :

Réseau

  1. #1
    Nouveau membre du Club
    Inscrit en
    Octobre 2013
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Octobre 2013
    Messages : 35
    Points : 25
    Points
    25
    Par défaut Récupération des données en TCP incomplète
    Bonsoir,

    Dans mon appli j'envoie une commande à un serveur en TCP puis je lis sa réponse.

    Le problème c'est que la réponse que je récupère est incomplète.

    J'envoie la commande "COP01\r\n"

    Je suis censé recevoir

    "COP01
    ~MSG01~Mon message
    @CV
    "

    or je ne recoit que "COP01"

    je fais un simple readAll dans un slot déclenché par readyRead()

    J'ai tout lu sur internet à ce propos, essayé toutes les options, attendre avant la lecture, faire une pause, boucler etc...
    rien a faire !

    comment gérez vous ce type de connexion ?

    merci à vous

  2. #2
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Août 2008
    Messages
    26 619
    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 619
    Points : 188 601
    Points
    188 601
    Par défaut


    En lisant le titre, j'aurais pensé que tu tentais de faire quelque chose de synchrone sur une API profondément asynchrone. Ce n'est pas le cas !

    Dans la doc http://qt-project.org/doc/qt-5/qiodevice.html#readyRead :

    This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
    Apparemment, le signal est émis chaque fois qu'un paquet arrive depuis l'OS : tu aurais donc le contenu du premier segment TCP transmis sur le réseau, mais pas l'entièreté des données transmises. Il te faut donc attendre que toutes les données soient arrivées avant de les traiter (notamment précisé dans http://qt-project.org/doc/qt-5.0/qtn...uneclient.html : «*QTcpSocket buffers up all incoming data and emits readyRead() for every new block that arrives, and it is our job to ensure that we have received all the data we need before we start parsing.*»). Je dirais donc de n'effectuer le readAll() qu'une fois toutes les données attendues reçues. Maintenant, ça fait un bail que je n'ai pas touché au réseau avec Qt, donc à prendre avec des pincettes.
    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
    Nouveau membre du Club
    Inscrit en
    Octobre 2013
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Octobre 2013
    Messages : 35
    Points : 25
    Points
    25
    Par défaut
    Hey !

    Merci pour ta réponse.
    Oui effectivement c'est ce que je me dis : attendre que toutes les données soient arrivées avant de les traiter...

    oui mais comment !?

    Sincèrement je tourne autour de cette question depuis des jours et je n'arrive pas à comprendre le concept, et j'ai l'impression de tourner autour du concept sans arriver à l'appliquer !

    - Je fais une écriture sur le socket.
    - cette action amène une réponse du serveur
    - cela déclenche un readyRead()
    - dans le slot je lis donc ce qui est arrivé : ici par un readAll() mais j'ai aussi tenté un read(), un readLine() etc...
    - théoriquement ce slot devrait se déclencher autant de fois que mon client recoit un morceau de la réponse ? or j'ai l'impression que ce n'est pas le cas.

    Dans l'idée j'essaie de faire :

    - écriture
    - attente de readyRead()
    - au déclenchement du signal readyRead() je lis ce qui est disponible sur le socket.
    - a partir d'ici comment tester si j'ai tout recu ou pas ? il n'y a rien dans ma trame de réponse qui me permette de le savoir. Je me base donc sur des signaux comme bytesAvailable() ou canReadLine() ou encode la méthode atEnd() ou que sais-je encore !

    comment traiter ces multiples morceaux ? avec une boucle ? avec des slots ?

    toute aide est a bienvenue.

    merci

  4. #4
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Août 2008
    Messages
    26 619
    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 619
    Points : 188 601
    Points
    188 601
    Par défaut
    Citation Envoyé par Marty69 Voir le message
    comment traiter ces multiples morceaux ? avec une boucle ? avec des slots ?
    Pour une classe asynchrone, les slots me semblent mieux : ce que tu connectes à readyRead() ajoute des données à un tampon ; quand ce slot détecte la fin des données, il émet un signal pour lancer le traitement effectif. Afin de détecter la fin des données, il faut bien avoir la valeur quelque part : une longueur dans l'en-tête (HTTP 1.0), un signal à la fin des données (HTTP 1.1 chunked encoding), une structure spécifique (XML, JSON), etc. (Ça me semble bien ba)

    Il n'est pas impossible que le code censé émettre les données ne soit pas parfait : il pourrait très bien mettre des données dans un tampon, attendre qu'il soit suffisamment rempli avant d'émettre sur le réseau.
    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
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Août 2008
    Messages
    26 619
    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 619
    Points : 188 601
    Points
    188 601
    Par défaut
    Citation Envoyé par Marty69 Voir le message
    comment traiter ces multiples morceaux ? avec une boucle ? avec des slots ?
    Pour une classe asynchrone, les slots me semblent mieux : ce que tu connectes à readyRead() ajoute des données à un tampon ; quand ce slot détecte la fin des données, il émet un signal pour lancer le traitement effectif dudit tampon. Afin de détecter la fin des données, il faut bien avoir la valeur quelque part : une longueur dans l'en-tête (HTTP 1.0), un signal à la fin des données (HTTP 1.1 chunked encoding), une structure spécifique (XML, JSON), etc. (Ça me semble bien bas niveau comme approche… QDataStream pourrait-il aider ?)

    Il n'est pas impossible que le code censé émettre les données ne soit pas parfait : il pourrait très bien mettre des données dans un tampon, attendre qu'il soit suffisamment rempli avant d'émettre sur le réseau.
    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 !

  6. #6
    Nouveau membre du Club
    Inscrit en
    Octobre 2013
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Octobre 2013
    Messages : 35
    Points : 25
    Points
    25
    Par défaut
    Ok.

    En fait mon problème se trouve peut être du côté du matériel.
    Toutes les infos que je lis sur les forums et ta réponse également préconise de boucler jusqu'a ce que la fin de la donnée envoyée soit détectée...

    or c'est la que je ne comprend pas, car moi de ce que je vois dans mes docs il n'y a aucune info dans la trame de réponse envoyée qui me permettrai de détecter cette fin de trame ! ou en tout cas cela n'est pas documenté !

    l'extrait de la doc :

    Cette réponse est constituée d’une ou plusieurs lignes de texte, chacune terminée par le code de fin de ligne EOL (CR/LF ...).
    La dernière de ces lignes a la syntaxe suivante : @<code> <résultat> EOL

    Dans cette ligne, <code> est une copié du premier caractère de la commande envoyée (par exemple ‘E’ si la commande était ‘ETA’). <résultat> peut être, soit la lettre ‘V’ (valide) si la commande a pu être exécutée sans erreur, soit la lettre ‘E’ (erreur) si une erreur s’est produite.
    J'ai essayé de faire une boucle avec attente du caractère "@" par exemple, mais ça boucle à l'infini, en gros comme si je ne recevais jamais ce caractère. Mais je pense qu'il doit y avoir un problème aussi dans mon code... Comment vous feriez cette boucle par exemple ?

    Aussi je me pose une question : la réponse est composée de plusieurs lignes avec un code de fin de ligne à chaque ligne (CR/LF). Cela peut il poser problème ? y a-t-il une manière de gérer cela ? parceque j'ai l'impression que je recoit la premiere ligne mais pas le reste.

    Je me pose aussi une question : quand j'envoie cette même commande via telnet dans un terminal, j'obtiens bien toute la réponse. Comment fait telnet pour savoir quand toute la réponse est arrivée ?

    merci

  7. #7
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    Je vais ptetre bein dire une connerie mais en lisant tes pots, tu fais une lecture ligne/ligne c'est donc normal que tu ne reçoive que la première ligne.Le signal ReadyRead est envoyé lorsqu'un appel à read ne sera pas bloquant, donc lorsqu'un EOL est detecté.Dans ta tram, trois EOL sont présents, je pense qu'il faut donc que tu lise trois fois les données afin d'avoir la tram complète non?
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  8. #8
    Nouveau membre du Club
    Inscrit en
    Octobre 2013
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Octobre 2013
    Messages : 35
    Points : 25
    Points
    25
    Par défaut
    Si c'est une connerie alors j'y ai pensé aussi

    pour moi (c'est peut être une erreur) un readAll() lis TOUT ce qu'il y a dans le buffer du socket, donc si il y a 3 x EOL il les prend, sinon il faut attendre 3 x le signal readyRead() et lire 3x le buffer.

    J'ai essayé 3x readAll() evidement au premier j'ai bien ma première ligne, les deux autres sont vides.

    J'ai essayé avec readLine() mais pas mieux.

    Et aussi j'ai tenter d'attendre 3 x readyRead() : la encore la première ligne arrive bien, mais ensuite c'est timeout pour les autres. Autrement dit il n'y a rien d'autre qui arrive...

  9. #9
    Membre émérite
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Points : 2 724
    Points
    2 724
    Billets dans le blog
    1
    Par défaut
    ça vient juste de me revenir, mais j'ai eu aussi ce genre de soucis avec QT, je recevais pas toute la tram dont j'avais besoin. Solution appliquée: ne pas utilisé le réseau de QT mais utiliser mon propre système de socket.

    Le soucis que j'avais:
    Un serveur coder par mes soins qui envoyait un fichier xml.
    Un client en Qt qui recevais ce fichier.

    Lorsque j'envoyais le fichier avec mon serveur, j'envoyais d'abord un int qui contenait la taille du fichier (je le recevais toujours).
    Par contre le fichier, je le recevais une fois de temps en temps. J'ai réussi à solutionner ce problème en mettant un timer coté serveur:
    Envois du int, attente de 100ms, envois du fichier. Et la, à coup sur je recevais mon fichier. J'ai bien conscience que c'est une méthode mc gyver mais bon au moins ça fonctionnait.

    Il faudrait regarder si lors de l'envois et reception si Qt ne fais pas un effacement lors du ReadAll qui expliquerait une perte de la tram. J'en avasi conclu que les socket Qt et les socket maison ne sont pas compatible (en effet Qt->Qt, je n'avais aucun soucis, Qt->maison, pas de soucis non plus, mais maison->Qt, je perdais un bout de la tram).
    Pas de solution, pas de probleme

    Une réponse utile (ou +1) ->
    Une réponse inutile ou pas d'accord -> et expliquer pourquoi
    Une réponse à votre question


  10. #10
    Nouveau membre du Club
    Inscrit en
    Octobre 2013
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Octobre 2013
    Messages : 35
    Points : 25
    Points
    25
    Par défaut
    Pfffff, je ne m'en sors pas...

    le plus simple si vous avez le temps / l'envie c'est de voir le projet dans son intégralité :

    https://github.com/martialgallorini/ledManager

    Je continue de mon côté.

    merci à vous !

  11. #11
    Nouveau membre du Club
    Inscrit en
    Octobre 2013
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Octobre 2013
    Messages : 35
    Points : 25
    Points
    25
    Par défaut
    Eh bien j'ai fini par m'en sortir mais j'ai encore du mal à comprendre.

    Finalement avec
    il me lit bien chaque ligne en entier.

    Franchement j'aurais tout essayé : read, readAll()... RIEN à part readLine() ne fonctionne !

    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 TCPClient::readyRead()
    {
        if(socket->waitForReadyRead(2000))
        {
           QString resp;
           while(socket->bytesAvailable() > 0)
           {
                resp.append(socket->readLine());
           }
           emit sigTcpDataReceived(resp.toUtf8());
        }
        else
        {
            qWarning() << "Waiting for data to read timed out. No data received !";
        }
    }
    Merci à tous pour votre aide !

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

Discussions similaires

  1. Récupération des données d'un formulaire
    Par placenargac dans le forum Balisage (X)HTML et validation W3C
    Réponses: 2
    Dernier message: 04/02/2006, 15h10
  2. récupération des données via une liste déroulante
    Par rahan_dave dans le forum Access
    Réponses: 1
    Dernier message: 13/10/2005, 12h27
  3. [HTML][FORMULAIRE] Probleme dans la récupération des données
    Par baddounet dans le forum Balisage (X)HTML et validation W3C
    Réponses: 6
    Dernier message: 15/08/2005, 18h51
  4. Réponses: 2
    Dernier message: 20/02/2004, 08h47
  5. Réponses: 13
    Dernier message: 20/03/2003, 08h11

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