Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to use QTcpSocket in static library to interact with non-Qt server

How to use QTcpSocket in static library to interact with non-Qt server

Scheduled Pinned Locked Moved Unsolved General and Desktop
12 Posts 4 Posters 3.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • FredTF Offline
    FredTF Offline
    FredT
    wrote on last edited by
    #3

    Hi SGaist,
    I took a look at QNetworkAccessManager, but I can't figure how to send an XML message to a server. How can I do that?

    This is my current class using QTcpSocket for now:

    Client.h

    #ifndef Client_H
    #define Client_H
    
    #include <QObject>
    #include <QTcpSocket>
    #include <QByteArray>
    
    #include <map>
    #include <set>
    
    #include "pugixml.hpp"
    
    class Client : public QObject
    {
        Q_OBJECT
    public:
        Client(QObject* parent = 0) : QObject(parent) {}
        Client(const QString& server, const QString& port, QObject* parent = 0);
    
        // Connection
        void connectToServer(const QString& server, const QString& port);
        void login(const QString& user, const QString& password, const QString& project);
        void logout();
    
        QString const getSessionId() { return sessionId_; }
    
        void throwOnError(const pugi::xml_document& doc);
    
        QString sessionId() const { return sessionId_; }
    
        QString outMessage;     // For testing
        QString resultMessage;  // For testing
    
    signals:
        void ready();
        void startReading();
        void finishedReading();
        void taskCompleted();
    
    private slots:
        void getResult();
    
    private:
        void sendMessage(const QString& message);    
    
        QTcpSocket socket_;
    
        QString sessionId_;
        QString projectName_;
    
        QByteArray result_;
    
    
    };
    
    #endif // Client_H
    

    Client.cpp

    #include "client.h"
    
    #include <pugixml.hpp>
    
    #include <map>
    #include <sstream>
    #include <string>
    
    #include "task.h" // Generate the API message, not relevant
    
    
    Client::Client(const QString& server, const QString& port, QObject* parent):
        QObject(parent)
    {
        connectToServer(server, port);
        connect(&socket_, &QTcpSocket::readyRead, this, &Client::getResult);
    }
    
    void Client::connectToServer(const QString& server, const QString& port)
    {
        bool ok;
        int portNumber = port.toInt(&ok);
    
        if (ok) {
            if (socket_.state() == QTcpSocket::SocketState::ConnectedState)
                socket_.disconnectFromHost();
            socket_.connectToHost(server, portNumber);
        } else {
            throw tr("Cannot connect to server %1 on port %2. Make sure the provided information are correct.")
                .arg(server)
                .arg(port);
        }
    }
    
    void Client::throwOnError(const pugi::xml_document& doc)
    {
        pugi::xpath_node_set errors = doc.select_nodes("/EXECUTION/TASK/RESULTSET/RESULT/MESSAGE");
    
        std::string error_message = "";
        for (pugi::xpath_node_set::const_iterator it = errors.begin(); it != errors.end(); ++it)
        {
            pugi::xml_node node = it->node();
            if (std::string(node.attribute("type").value()) == "Error" ||
                std::string(node.attribute("type").value()) == "Warning")
                error_message += node.child_value();
        }
    
        if (!error_message.empty())
            throw std::exception(error_message.c_str());
    }
    
    void Client::sendMessage(const QString &message)
    {
        outMessage = message;
        result_.clear();
        socket_.write(message.toUtf8());
    }
    
    void Client::getResult()
    {
        emit startReading();
        while (socket_.bytesAvailable() > 0) {
            result_.append(socket_.readAll());
            socket_.flush();
        }
    
        resultMessage = QString(result_);
        emit finishedReading();
    }
    
    void Client::login(const QString& user, const QString& password, const QString& project)
    {
        std::map<QString, QString> whereFields {{"userName", user}, {"password", password}};
        QString request = prepareMessage("Login", "Security", std::map<QString, QString>(), whereFields); // Generates the XML message for the API
        sendMessage(request);
    
        // Wait for data to arrive - How ?
    
        std::stringstream xmlResult = getXmlData(result_); // Remove the header from the API response and convert the QByteArray to a std::stringstream
    
        pugi::xml_document doc;
        pugi::xml_parse_result result = doc.load(xmlResult);
    
        throwOnError(doc);
    
        pugi::xpath_node session = doc.select_node("/EXECUTION/TASK/RESULTSET/DATASETS/DATASET/secId");
        sessionId_ = QString::fromStdString(session.node().first_child().value());
        projectName_ = project;
    
        emit taskCompleted();
    }
    
    VRoninV 1 Reply Last reply
    0
    • FredTF FredT

      Hi SGaist,
      I took a look at QNetworkAccessManager, but I can't figure how to send an XML message to a server. How can I do that?

      This is my current class using QTcpSocket for now:

      Client.h

      #ifndef Client_H
      #define Client_H
      
      #include <QObject>
      #include <QTcpSocket>
      #include <QByteArray>
      
      #include <map>
      #include <set>
      
      #include "pugixml.hpp"
      
      class Client : public QObject
      {
          Q_OBJECT
      public:
          Client(QObject* parent = 0) : QObject(parent) {}
          Client(const QString& server, const QString& port, QObject* parent = 0);
      
          // Connection
          void connectToServer(const QString& server, const QString& port);
          void login(const QString& user, const QString& password, const QString& project);
          void logout();
      
          QString const getSessionId() { return sessionId_; }
      
          void throwOnError(const pugi::xml_document& doc);
      
          QString sessionId() const { return sessionId_; }
      
          QString outMessage;     // For testing
          QString resultMessage;  // For testing
      
      signals:
          void ready();
          void startReading();
          void finishedReading();
          void taskCompleted();
      
      private slots:
          void getResult();
      
      private:
          void sendMessage(const QString& message);    
      
          QTcpSocket socket_;
      
          QString sessionId_;
          QString projectName_;
      
          QByteArray result_;
      
      
      };
      
      #endif // Client_H
      

      Client.cpp

      #include "client.h"
      
      #include <pugixml.hpp>
      
      #include <map>
      #include <sstream>
      #include <string>
      
      #include "task.h" // Generate the API message, not relevant
      
      
      Client::Client(const QString& server, const QString& port, QObject* parent):
          QObject(parent)
      {
          connectToServer(server, port);
          connect(&socket_, &QTcpSocket::readyRead, this, &Client::getResult);
      }
      
      void Client::connectToServer(const QString& server, const QString& port)
      {
          bool ok;
          int portNumber = port.toInt(&ok);
      
          if (ok) {
              if (socket_.state() == QTcpSocket::SocketState::ConnectedState)
                  socket_.disconnectFromHost();
              socket_.connectToHost(server, portNumber);
          } else {
              throw tr("Cannot connect to server %1 on port %2. Make sure the provided information are correct.")
                  .arg(server)
                  .arg(port);
          }
      }
      
      void Client::throwOnError(const pugi::xml_document& doc)
      {
          pugi::xpath_node_set errors = doc.select_nodes("/EXECUTION/TASK/RESULTSET/RESULT/MESSAGE");
      
          std::string error_message = "";
          for (pugi::xpath_node_set::const_iterator it = errors.begin(); it != errors.end(); ++it)
          {
              pugi::xml_node node = it->node();
              if (std::string(node.attribute("type").value()) == "Error" ||
                  std::string(node.attribute("type").value()) == "Warning")
                  error_message += node.child_value();
          }
      
          if (!error_message.empty())
              throw std::exception(error_message.c_str());
      }
      
      void Client::sendMessage(const QString &message)
      {
          outMessage = message;
          result_.clear();
          socket_.write(message.toUtf8());
      }
      
      void Client::getResult()
      {
          emit startReading();
          while (socket_.bytesAvailable() > 0) {
              result_.append(socket_.readAll());
              socket_.flush();
          }
      
          resultMessage = QString(result_);
          emit finishedReading();
      }
      
      void Client::login(const QString& user, const QString& password, const QString& project)
      {
          std::map<QString, QString> whereFields {{"userName", user}, {"password", password}};
          QString request = prepareMessage("Login", "Security", std::map<QString, QString>(), whereFields); // Generates the XML message for the API
          sendMessage(request);
      
          // Wait for data to arrive - How ?
      
          std::stringstream xmlResult = getXmlData(result_); // Remove the header from the API response and convert the QByteArray to a std::stringstream
      
          pugi::xml_document doc;
          pugi::xml_parse_result result = doc.load(xmlResult);
      
          throwOnError(doc);
      
          pugi::xpath_node session = doc.select_node("/EXECUTION/TASK/RESULTSET/DATASETS/DATASET/secId");
          sessionId_ = QString::fromStdString(session.node().first_child().value());
          projectName_ = project;
      
          emit taskCompleted();
      }
      
      VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by
      #4

      @FredT said in How to use QTcpSocket in static library to interact with non-Qt server:

      but I can't figure how to send an XML message to a server.

      http://doc.qt.io/qt-5/qnetworkaccessmanager.html#post-1

      QByteArray dataBuffer;
      QXmlStreamWriter xmlWrite(&dataBuffer);
      // use xmlWrite to write stuff in xml
      qnam->post(QNetworkRequest("http://mysite.com"),dataBuffer);
      

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      1 Reply Last reply
      1
      • FredTF Offline
        FredTF Offline
        FredT
        wrote on last edited by
        #5

        Hi VRonin,

        Thanks, I'll llook into this.

        One thing I fail to mention is that it is not a Web server. It is a server running on the internal network and to communicate with it I need to send messages with a custom header.

        From the look of it, QNetworkAccessManager seems to be oriented towards Web programming. Is there any way to use it with a custom protocol?

        VRoninV 1 Reply Last reply
        0
        • FredTF FredT

          Hi VRonin,

          Thanks, I'll llook into this.

          One thing I fail to mention is that it is not a Web server. It is a server running on the internal network and to communicate with it I need to send messages with a custom header.

          From the look of it, QNetworkAccessManager seems to be oriented towards Web programming. Is there any way to use it with a custom protocol?

          VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #6

          @FredT said in How to use QTcpSocket in static library to interact with non-Qt server:

          Web programming

          no, you can use it in local addresses but it handles known protocols.

          For custom protocols QTcpSocket is indeed the right choice, you can pass the socket directly to QXmlStreamWriter constructor and use that wo send the data

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          FredTF 1 Reply Last reply
          0
          • VRoninV VRonin

            @FredT said in How to use QTcpSocket in static library to interact with non-Qt server:

            Web programming

            no, you can use it in local addresses but it handles known protocols.

            For custom protocols QTcpSocket is indeed the right choice, you can pass the socket directly to QXmlStreamWriter constructor and use that wo send the data

            FredTF Offline
            FredTF Offline
            FredT
            wrote on last edited by
            #7

            Thanks for the information.

            Currently I am using a QString to send the XML message because I also need to send a header as well for the protocol as well. Also, worth noting, not all answers from the server are in XML - depending on the request they can also be sent in a custom format.

            So I guess I am back to my initial problem: how can I wait for all the data to be there before processing it? This is actually the only point I am stuck at.

            jsulmJ VRoninV 2 Replies Last reply
            0
            • FredTF FredT

              Thanks for the information.

              Currently I am using a QString to send the XML message because I also need to send a header as well for the protocol as well. Also, worth noting, not all answers from the server are in XML - depending on the request they can also be sent in a custom format.

              So I guess I am back to my initial problem: how can I wait for all the data to be there before processing it? This is actually the only point I am stuck at.

              jsulmJ Offline
              jsulmJ Offline
              jsulm
              Lifetime Qt Champion
              wrote on last edited by
              #8

              @FredT said in How to use QTcpSocket in static library to interact with non-Qt server:

              So I guess I am back to my initial problem: how can I wait for all the data to be there before processing it?

              Doesn't this depend on your protocol? I mean: there must be some notion of "END OF DATA" in what the server is sending to you (all what you send to server). Just read everything into a buffer until you get this "END OF DATA" and then process it. Use http://doc.qt.io/qt-5/qiodevice.html#readyRead signal to get the notification that there is new data and read it using http://doc.qt.io/qt-5/qiodevice.html#readAll

              https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              1
              • FredTF FredT

                Thanks for the information.

                Currently I am using a QString to send the XML message because I also need to send a header as well for the protocol as well. Also, worth noting, not all answers from the server are in XML - depending on the request they can also be sent in a custom format.

                So I guess I am back to my initial problem: how can I wait for all the data to be there before processing it? This is actually the only point I am stuck at.

                VRoninV Offline
                VRoninV Offline
                VRonin
                wrote on last edited by
                #9

                @FredT said in How to use QTcpSocket in static library to interact with non-Qt server:

                Currently I am using a QString to send the XML message because I also need to send a header as well for the protocol as well.

                Not necessary, you can use a QTextStream and a QXmlStreamWriter on the same socket

                how can I wait for all the data to be there before processing it?

                QDataStream socketStream(socket);
                socketStream.startTransaction();
                // read data using socketStream
                if(socketStream.commitTransaction()){
                // all data was received and it was ok
                }
                else{
                // not enough data or something went wrong
                }
                

                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                ~Napoleon Bonaparte

                On a crusade to banish setIndexWidget() from the holy land of Qt

                1 Reply Last reply
                1
                • FredTF Offline
                  FredTF Offline
                  FredT
                  wrote on last edited by
                  #10

                  @jsulm
                  The problem with the end of data is that it is relative to the message sent. Furthermore, there might be extra characters at the end of the message (usually spaces) and other times the data will be incomplete for random reasons (for instance a tag that does not exist in the system will not be skipped or no error message will be sent but a partial data will be sent)

                  @VRonin
                  Here is my first try with the QDataStream as you suggested:

                  void Client::login(const QString& user, const QString& password, const QString& project)
                  {
                      std::map<QString, QString> whereFields {{"userName", user}, {"password", password}};
                      QString request = prepareMessage("Login", "Security", std::map<QString, QString>(), whereFields); // Generates the XML message for the API
                      sendMessage(request);
                  
                     QDataStream socketStream(&socket_);
                  
                      socketStream.startTransaction();
                      while (!socketStream.commitTransaction()) {
                          continue;
                      }
                  
                      QString socketResult;
                      socketStream >> socketResult;
                      qDebug() << socketResult;
                  
                      std::stringstream xmlResult = getXmlData(result_); // Remove the header from the API response and convert the QByteArray to a std::stringstream
                  
                      pugi::xml_document doc;
                      pugi::xml_parse_result result = doc.load(xmlResult);
                  
                      throwOnError(doc);
                  
                      pugi::xpath_node session = doc.select_node("/EXECUTION/TASK/RESULTSET/DATASETS/DATASET/secId");
                      sessionId_ = QString::fromStdString(session.node().first_child().value());
                      projectName_ = project;
                  
                      emit taskCompleted();
                  }
                  

                  In this case the socketResult is empty.

                  My second try:

                  void Client::getResult()
                  {
                      socketStream_ >> result_;
                  
                      if (!socketStream_.commitTransaction())
                          return;
                  
                      resultMessage = QString(result_);
                      emit finishedReading();
                  }
                  
                  void Client::login(const QString& user, const QString& password, const QString& project)
                  {
                      std::map<QString, QString> whereFields {{"userName", user}, {"password", password}};
                      QString request = prepareMessage("Login", "Security", std::map<QString, QString>(), whereFields); // Generates the XML message for the API
                      sendMessage(request);
                  
                     QEventLoop loop;
                      connect(this, &MapleClient::finishedReading, &loop, &QEventLoop::quit);
                      loop.exec();
                  
                      std::stringstream xmlResult = getXmlData(result_); // Remove the header from the API response and convert the QByteArray to a std::stringstream
                  
                      pugi::xml_document doc;
                      pugi::xml_parse_result result = doc.load(xmlResult);
                  
                      throwOnError(doc);
                  
                      pugi::xpath_node session = doc.select_node("/EXECUTION/TASK/RESULTSET/DATASETS/DATASET/secId");
                      sessionId_ = QString::fromStdString(session.node().first_child().value());
                      projectName_ = project;
                  
                      emit taskCompleted();
                  }
                  

                  However this time it gets stuck at loop.exec(); and the getResult slot is never triggered.

                  Can you explain me what I am doing wrong, please?

                  1 Reply Last reply
                  0
                  • VRoninV Offline
                    VRoninV Offline
                    VRonin
                    wrote on last edited by
                    #11

                    while (!socketStream.commitTransaction()) { continue; }

                    That's not how transaction works

                    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                    ~Napoleon Bonaparte

                    On a crusade to banish setIndexWidget() from the holy land of Qt

                    1 Reply Last reply
                    0
                    • FredTF Offline
                      FredTF Offline
                      FredT
                      wrote on last edited by
                      #12

                      Well, I tried both cases and I did not see any difference. My initial try was

                      void Client::login(const QString& user, const QString& password, const QString& project)
                      {
                          std::map<QString, QString> whereFields {{"userName", user}, {"password", password}};
                          QString request = prepareMessage("Login", "Security", std::map<QString, QString>(), whereFields); // Generates the XML message for the API
                          sendMessage(request);
                          socketStream.startTransaction();
                          if (socketStream.commitTransaction()) {
                              QString socketResult;
                              socketStream >> socketResult;
                              qDebug() << socketResult;
                          } else {
                              throw "Error!"
                          }
                      }
                      

                      But regardless of the way I did it the socketResult was empty.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved