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.2k 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.
  • SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by
    #2

    Hi,

    Sounds like you should rather use QNetworkAccessManager.

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    1 Reply Last reply
    0
    • 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