C++ Qt:创建一个库来连接到外部资源,并在不同的应用程序中使用它

C++ Qt:创建一个库来连接到外部资源,并在不同的应用程序中使用它,c++,qt,qtcpsocket,C++,Qt,Qtcpsocket,我试图实现一个库来连接到外部资源,并通过基于XML的API与之交互。然而,我不知道如何在处理之前获取所有数据。我正在Windows上使用QTcpSocket 我与资源互动的方式如下: 连接到服务器 发送带有凭据的XML消息 从服务器获取响应 解析XML响应以提取会话ID 用包含会话ID的XML发送其他请求,并解析XML响应 在我的应用程序中使用结果 当然,根据请求,消息将不会具有相同的结构,因此我需要在相同的函数中解析XML,对吗?如果是这样的话,我怎么能等到所有数据到达后再对其进行解析呢 此外

我试图实现一个库来连接到外部资源,并通过基于XML的API与之交互。然而,我不知道如何在处理之前获取所有数据。我正在Windows上使用QTcpSocket

我与资源互动的方式如下:

  • 连接到服务器
  • 发送带有凭据的XML消息
  • 从服务器获取响应
  • 解析XML响应以提取会话ID
  • 用包含会话ID的XML发送其他请求,并解析XML响应
  • 在我的应用程序中使用结果
  • 当然,根据请求,消息将不会具有相同的结构,因此我需要在相同的函数中解析XML,对吗?如果是这样的话,我怎么能等到所有数据到达后再对其进行解析呢

    此外,如前所述,它是在Windows环境下,因此我不能使用“waitForReadyRead”,因为如中所述:

    此功能在Windows上可能会随机失败。如果您的软件将在Windows上运行,请考虑使用事件循环和Read Yead()信号。 它是如何工作的

    谢谢, 弗雷德

    编辑 这是我目前的代码:

    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();
    }
    
    #包括“client.h”
    #包括
    #包括
    #包括
    #包括
    #包括“task.h”//生成API消息,不相关
    客户端::客户端(常量字符串和服务器、常量字符串和端口、QObject*父级):
    QObject(父对象)
    {
    connectToServer(服务器、端口);
    连接(&socket;&qtcsocket::readyRead,this,&Client::getResult);
    }
    无效客户端::connectToServer(常量QString和服务器、常量QString和端口)
    {
    布尔ok;
    int-portNumber=port.toInt(&ok);
    如果(确定){
    if(socket.state()==qtcsocket::SocketState::ConnectedState)
    socket_u.disconnectFromHost();
    套接字连接到主机(服务器、端口号);
    }否则{
    抛出tr(“无法连接到端口%2上的服务器%1。请确保提供的信息正确。”)
    .arg(服务器)
    .arg(港口);
    }
    }
    无效客户端::throwOnError(const pugi::xml\u文档和文档)
    {
    pugi::xpath_node_set errors=doc。选择_节点(“/EXECUTION/TASK/RESULTSET/RESULT/MESSAGE”);
    std::字符串错误_message=“”;
    for(pugi::xpath_node_set::const_iterator it=errors.begin();it!=errors.end();+it)
    {
    pugi::xml_node node=it->node();
    如果(std::string(node.attribute(“type”).value())=“错误”||
    std::string(node.attribute(“type”).value()=“警告”)
    错误消息+=node.child_值();
    }
    如果(!error_message.empty())
    抛出std::exception(error_message.c_str());
    }
    无效客户端::发送消息(常量QString和消息)
    {
    outMessage=消息;
    结果清除();
    socket.write(message.toUtf8());
    }
    void客户端::getResult()
    {
    发射startreding();
    while(套接字字节可用()>0){
    result.append(socket.readAll());
    插座uu.flush();
    }
    resultMessage=QString(result_u2;);
    发出令人恐惧的声音;
    }
    无效客户端::登录(const-QString和用户、const-QString和密码、const-QString和项目)
    {
    映射whereFields{{“userName”,user},{“password”,password};
    QString request=prepareMessage(“Login”,“Security”,std::map(),whereFields);//为API生成XML消息
    发送消息(请求);
    //等待数据到达-如何?
    std::stringstream xmlResult=getXmlData(result);//从API响应中删除头并将QByteArray转换为std::stringstream
    pugi::xml_文档文档;
    pugi::xml_parse_result=doc.load(xmlResult);
    错误(doc);
    pugi::xpath_node session=doc。选择_节点(“/EXECUTION/TASK/RESULTSET/DATASET/DATASET/secId”);
    sessionId_QString::fromStdString(session.node().first_child().value());
    projectName=项目;
    emit taskCompleted();
    }
    
    客户: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
    
    \ifndef客户端
    #定义客户端
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括“pugixml.hpp”
    类客户端:公共QObject
    {
    Q_对象
    公众:
    客户端(QObject*parent=0):QObject(parent){}
    客户端(常量字符串和服务器,常量字符串和端口,QObject*parent=0);
    //联系
    无效连接到服务器(常量字符串和服务器、常量字符串和端口);
    无效登录(const-QString和用户、const-QString和密码、const-QString和项目);
    无效注销();
    QString常量getSessionId(){return sessionId_;}
    void throwOnError(const pugi::xml_文档和文档);
    QString sessionId()常量{return sessionId_;}
    QString outMessage;//用于测试
    QString resultMessage;//用于测试
    信号:
    void ready();
    void startreding();
    无效完成恐惧();
    void taskCompleted();
    专用插槽:
    void getResult();
    私人:
    无效发送消息(常量字符串和消息);
    QTcpSocket插座;
    QString sessionId;
    QString项目名称;
    QByteArray结果;
    };
    #endif//Client_H
    
    所有
    waitForXyz
    方法都应被视为断开代码的移植辅助工具。在很少的情况下使用它们是正确的,我还没有看到任何这样的问题出现。不要等待,而是将插槽连接到
    readyRead
    信号,以便在新数据可用时异步运行代码,并将其收集到
    QByteArray
    缓冲区中。增量解析该缓冲区,直到解析器指示最顶端的元素已关闭。我可以使用此方法获取数据,但我不确定如何等待才能处理数据-如果我不清楚,请原谅,我试图实现的是找到一种方法来知道数据何时可用以开始处理它。我已经说过如何:将插槽连接到套接字的
    readyRead
    信号。每次数据块到达时都会通知您。您需要将这些块拼接在一起并解析它们。取决于