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 :

[QTcpSocket en écriture] Pour dialoguer QIODevice ou QDataStream ?


Sujet :

Réseau

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut [QTcpSocket en écriture] Pour dialoguer QIODevice ou QDataStream ?
    Bonjour,

    Je suis un habitué du C et des MFC. J'ai voulu profiter de l'occasion de développer une petite IHM client pour utiliser QT.
    J'ai deux questions :
    _ le signal hostFound correspond-il à la résolution d'adresse IP (ie conversion d'un nom "google"->IP ? Je pose la question car lorsque j'entre une adresse IP locale invalide (machine inexistante) je rentre toujours dans cet état.

    _ Après contrôle avec wireshark j'ai vu que les octets réseau de ma trame (struct TRAME : 3 int32) étaient envoyés "little endian" (sur mon intel).
    Pour l'écriture j'utilise la méthode du parent QIODevice::write() pour reprendre les exemples de la doc QT.

    Voici le bout de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    	m_stFrame.order = START ;
    	// Get parameters and send it to our boy
    	m_stFrame.bUsedFixedData = (ui.rbFixedData->isChecked()) ? 1 : 0 ;
    	m_stFrame.uiDelay = ui.sbDelayTransmission->value() ;
    	bytesToWrite = sizeof(m_stFrame) - m_hostSckt.write((const char*)&m_stFrame, sizeof(m_stFrame)) ;
    	if (bytesToWrite == -1)
    	{
    		on_socketError() ;
    	}
    Maintenant qu'est-il préférable de faire ?
    • une méthode "convert_frame()" dans ma classe avec des appels à htons()/htonl()
    • Surcharger la classe QDataStream pour définir un opérateur <<(struct TRAME&)


    Que pourrait réellement apporter QDataStream dans le cadre de ce développement ? En fait j'hésite à utiliser cette classe car je crains de perdre le contrôle des écritures sur la socket.

    Voilà merci pour vos retours d'expérience.

    edit : Parfois en articulant nos questions on y trouve des réponses.
    Si je surcharge QDataStream avec ma fonction << probablement que j'utiliserai la fonction operator<< ( qint32 i ). Mais ensuite comment est envoyé le message ? Est-ce qu'en procédant ainsi je force l'écriture de plusieurs trames ou les données sont bufferisées ?
    S'agit-il d'une écritures synchrone qui peut bloquer mon IHM ?


    J'ai encore une autre question :
    Pour l'instant j'utilise la méthode write() et un slot pour gérer le signal bytesWritten(qint64 bytes) et écrire le reste des données (reprise de l'exemple QT network dialog

    Pourtant dans ce même exemple je trouve l'appel à la fonction QIODevice::bytesToWrite () ce qui me laisse penser que les données à envoyer passées par la fonction write() ont été bufferisées et seront écrites par l'API (ce que je trouve bizarre parce le buffer peut ne récupérer qu'une partie du message si celui-ci est trop grand) . Alors pourquoi refaire des appels à write() pour écrire le restant du message ?

  2. #2
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    C'est quand étrange d'utiliser une autre classe pour les écritures/lecture alors que TCPSocket hérite de Qiodevice qui se charge déjà de ces opérations.

  3. #3
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Bonjour bizulk

    Pour répondre simplement à la question posée dans le titre : tu as besoin des 2 classes.

    Plutôt que de créer un seule classe qui permet d'envoyer tout type de données vers un device (réseau, fichier, périphérique, etc.), Qt a fait le choix de partager les responsabilités dans 2 classes différentes :

    - QIODevice (dont hérite QTcpSocket) est une classe d'interface permettant d'avoir une structure unique pour envoyer et recevoir des flux de données bas niveau (ces flux étant contenus dans des QByteArray, c'est à dire des tableaux d'octets). Cette classe permet donc de pouvoir travailler avec n'importe quel device de la même façon, sans se préoccuper de ce qu'il y a derrière.

    Sa responsabilité est donc : "Envoyer ou recevoir un flux de données vers un périphérique"

    - QDataStream permet de sérialiser des données vers un QIODevice, c'est à dire transformer des objets Qt/C++ (soient des types de bases tels que int, double, QString, soient des classes créées par l'utilisateur) en flux de données. La particularité de la sérialisation est de convertir les données en format bas-level indépendant de la plateforme, ce qui garantie que les données ne sont pas mal interprété lors des transferts.

    Sa responsabilité est donc : "Convertir des données au format Qt/C++ en un flux de données"


    Donc, pour faire ce que tu souhaites, tu dois créer une connection avec QIODevice puis écrire dessus avec QDataStream.

    La raison de ce choix de séparation des responsabilités est très simple : si on souhaite ajouter un nouveau device, on n'est pas obligé de réécrire les fonctions de sérialisation. De même, si on souhaite ajouter un nouveau type de sérialisation, on n'est pas obligé de modifier toutes les classes QIODevice.

    Pour le paramétrage de la conversion des données (little ou big endian, etc.), c'est paramétrable dans QDataStream (si tu as du little endian, c'est que tes données sont codé dans l'ordinateur comme ça et qu'avec la méthode que tu utilises, il les envoie telle quelle)

    le signal hostFound correspond-il à la résolution d'adresse IP (ie conversion d'un nom "google"->IP ? Je pose la question car lorsque j'entre une adresse IP locale invalide (machine inexistante) je rentre toujours dans cet état.
    Le signal hostFound est émis lorsque la connection a réussie normalement.


    La méthode pour écrire dans un QIODevice avec un QDataStream est la suivante (extrait de l'exemple Fortune Client) :
    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
    tcpSocket = new QTcpSocket(this); // tu crées ton socket
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readDataFromTcp())); // tu connectes le signal renvoyé par le serveur à ton slot de lecture
     
    tcpSocket->abort(); // tu stoppes d'éventuelles connections en cours
    tcpSocket->connectToHost(hostLineEdit->text(), portLineEdit->text().toInt()); // tu te connectes au TCP coté serveur
     
    (...)
    void TaClasse::readDataFromTcp()
    {
       QDataStream in(tcpSocket); // tu crées un QDataStream pour lire sur ta connection TCP
     
       if (blockSize == 0)  // tu lis la taille du bloc de données envoyé
       {
             if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
                 return;
     
             in >> blockSize;
        }
     
        if (tcpSocket->bytesAvailable() < blockSize) // tu attends d'avoir toutes tes données
             return;
     
        quint32 data;
        in >> data; // tu lis tes données
    }
    Pour l'écriture, le principe est très proche, regarde l'exemple Fortune Serveur


    Conceptuellement, tu dois hériter de QIODevice ou de QDataStream pour créer un nouveau type de connection ou une nouvelle méthode de sérialisation. Ce qui n'est pas ton cas : tu dois juste utiliser les différents formats proposer par QDataStream (http://qt.developpez.com/doc/4.6/datastreamformat/).

    Par contre, tu peux ajouter dans tes propres classes les opérateurs << et >> pour pouvoir les écrire directement dans QDataStream.


    Pour les autres questions : write(), bytesToWrite(), etc. sont des fonctions bas-level que tu n'est pas sensé utiliser (sauf si tu créés tes propres classes dérivant de QDataStream)
    Par défaut, la connection est asynchrone

  4. #4
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Merci pour cette réponse qui a entre autre le mérite de séparer clairement les responsabilités des deux classes.

    Le signal hostFound est émis lorsque la connection a réussie normalement.
    Je pourrais montrer le code. Lorsque je fais le test sur une @IP non existante (donc pas dans la table ARP) de mon réseau la socket rentre bien dans cette état et du coup j'attends indéfiniment la connexion (j'ai rajoute un timeout pour referme la socket). A mon avis cet état sert simplement à dire : "j'ai convertit le nom de l'hôte en adresse IP".
    Ce qui m'ennuie c'est que avec les fonctions C on peut retourner les erreurs suivantes lors de la connexion :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    ETIMEDOUT
        Dépassement du délai maximum pendant la connexion. 
    ENETUNREACH
        Le réseau est inaccessible. 
    EHOSTUNREACH
        Le correspondant ne peut pas être joint.
    ...
    Ce qui n'est pas ton cas : tu dois juste utiliser les différents formats proposer par QDataStream (http://qt.developpez.com/doc/4.6/datastreamformat/).
    Par contre, tu peux ajouter dans tes propres classes les opérateurs << et >> pour pouvoir les écrire directement dans QDataStream.
    Mon réflexe a été de créer une structure pour écrire le contenu (3 qint32). Mais en te lisant je comprends que j'aurai pu :
    • Ne pas définir une structure et directement envoyer les 3 qint32 dans dans le qdatastream. L'avantage est la réduction du code mais ça me gêne car je ne vois pas clairement la définition de ma trame.
    • Définir dans ma classe la méthode << qui envoie dans le datastream une variable de type T_MYFRAME. Je conserve ainsi la une definition explicite de la trame.



    write(), bytesToWrite(), etc. sont des fonctions bas-level que tu n'est pas sensé utiliser (sauf si tu créés tes propres classes dérivant de QDataStream)
    Trop tard ces fonctions se gérant similairement au code socket écris en C je m'en suis servi en me basant sur un exemple cité plus haut de Qt. Néanmoins pour revenir à la méthode comment est gérée la mise en tampon ? Lorsque je vais écrire :
    mysocketStream << myFrame, l'API se charge-t-elle de gérer l'ensemble du message ?
    Que se passe-t-il s'il y a une erreur de transmission des données ? le signal error(QAbstractSocket::SocketError) sera-t-il émit ?

    Merci.

  5. #5
    Membre éprouvé
    Avatar de _skip
    Homme Profil pro
    Développeur d'applications
    Inscrit en
    Novembre 2005
    Messages
    2 898
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur d'applications
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Novembre 2005
    Messages : 2 898
    Par défaut
    Si c'est ce que tu demandes, les fonctions d'écritures sur les Sockets sont toutes bufferisées, tu peux utiliser la fonction flush pour forcer l'envoi effectif.

    Par ailleurs oui, le signal error est lancé si la connexion échoue ou si une erreur de transfert survient.
    Si tu souhaites travailler en synchrone, tu peux utiliser les fonctions waitForBytesWritten puis sitôt après tester la fonction error().

  6. #6
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Bonjour,

    En fait dans la situation suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    QDataStream out(tcpSocket);
    typedef struct
    {
     
    }
    out << myFrame

  7. #7
    Membre éclairé

    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Février 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2005
    Messages : 464
    Par défaut
    Bonjour,
    Non je ne cherche pas à travailler synchrone, j'essaie justement d'utiliser la mécanique asynchrone et de comprendre comment elle marche par rapport à mes connaissance sur la progr socket en C.

    En fait dans la situation suivante (code approximatif) :
    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
    typedef struct
    {
     int i
     int i2
    } T_FRAME
    QDataStream out(tcpSocket);
     
    QDataStream & operator<<  (T_FRAME & frame )
    {
       QDataStream stream ;
       stream  << frame.i << frame.i2
     
    }
     
    fonction onSend(void)
    {
     out << myFrame
    }
    L'API va-t-elle se charger d'envoyer le message en entier, contrairement à l'API bas niveau ou l'on doit vérifier le nombre d'octets effectivement écrits ?
    Les erreurs Qtcpsocket sont-elles bien remontées même en passant par Qdatastream ?

    En fait je pense que je vais faire l'essai y'a rien de plus simple pour répondre à ces questions ?

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 24/11/2006, 19h40
  2. [Apache] donner les droits d'écriture pour PHP
    Par Torpedox dans le forum Apache
    Réponses: 4
    Dernier message: 07/01/2006, 15h01
  3. Problème pour dialoguer avec port parallèle
    Par jejerome dans le forum C++
    Réponses: 8
    Dernier message: 16/05/2005, 11h13
  4. A la recherche d'un soft pour dialoguer sur port serie
    Par Rudy 34 dans le forum Composants
    Réponses: 5
    Dernier message: 25/02/2005, 15h54
  5. recherche composant pour dialoguer avec modem
    Par newbie qui galere dans le forum Bases de données
    Réponses: 1
    Dernier message: 15/10/2004, 23h20

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