QT中HTTP请求的同步和异步接口

在QT中,要执行HTTP请求,获取HTTP服务器的响应数据,需要启用network模块:

bash 复制代码
QT += network

QT的network模块主要有以下四个类:

1.QNetworkAccessManager: 该类的主要功能是处理网络请求和回复,管理网络接口,以及处理多线程网络操作;

2.QNetworkRequest:该类主要用于主要用于创建和管理HTTP请求;

3.QUrl:该类用于处理URL,提供了丰富的功能来解析、构造、操作URL;

4.QNetworkReplay:该类用于处理网络请求的响应。

1.常规异步接口

QT文档给出了使用这几个类进行HTTP请求的标准用法:

cpp 复制代码
QString urlStr = QString("http://www.sina.com.cn/");
QUrl url(urlStr);
QNetworkAccessManager httpClient;
QNetworkRequest request(url);
//request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* reply = httpClient.get(request);
QObject::connect(reply, &QNetworkReply::finished, this, [&](){
    if(reply->error() == QNetworkReply::NoError){
        //HTTP服务器返回正常200      
        //获取返回数据   
        QByteArray responseBytes = reply->readAll();   
    }
    else{    
        //HTTP服务器返回错误         
        //获取错误信息
        QString err = reply->errorString();   
    }
}

2.自定义同步接口

异步接口用起来也蛮方便,但是有时候也需要同步接口。要将以上的标准用法改成同步接口,需要注意两点:

1.调用接口的代码不一定在有事件循环的线程,所以需要使用QEventLoop自定义事件循环,避免调用接口时,因为无事件循环导致调用方代码卡死;

2.既然是同步接口访问可能发送阻塞的网络资源,就要加上超时参数。

以下给出了同步函数的例子:

cpp 复制代码
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QEventLoop>
#include <QTimer>
#include <QString>
#include <chrono>

bool HttpRequestSync(QString& urlStr, QString& retStr, QString& err, int timeout = 2000)
{
    QUrl url(urlStr);
    QNetworkRequest request(url);
    //request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

    QNetworkAccessManager httpClient;
    QNetworkReply* reply = httpClient.get(request);
    
    //HTTP请求执行完成标志
    bool okFinish = false;
    
    //定时器超时标志
    bool okTimeout1 = false;
    bool okTimeout2 = false;
    
    QEventLoop loop; //用于自定义事件循环
    QTimer timer;    //用于超时退出
    bool ret = false;
    err = "";
    QObject::connect(reply, &QNetworkReply::finished, &loop, [&](){
        timer.stop();
        if(reply->error() == QNetworkReply::NoError)
        {
            retStr = reply->readAll();
            ret = true;
        }
        else
        {
            err = reply->errorString();     
        }

        okFinish = true;
        loop.exit(); //退出时间循环
    });
    QObject::connect(&timer, &QTimer::timeout, &loop, [&](){
        okTimeout1 = true;
        err = " timeout";
        reply->abort();
        okTimeout2 = true;
    });
    timer.setSingleShot(true);
    timer.start(timeout);

    loop.exec(); //进入事件循环,等待异步函数执行完毕

    while(!okFinish || (okTimeout1 && !okTimeout2)) //超时事件发生后,等待定时器执行完毕
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    reply->deleteLater();
    return ret;
}

3.自定义异步任务

有了异步接口和同步接口,应用HTTP请求就会很方便了。但是,异步接口编程的代码看起来还是有些凌乱。另外,实际编程任务重,有些HTTP请求并不需要立即获取结果,只需要在某个时候查询即可。所以,可以考虑将异步接口封装成异步任务,这种异步任务有以下特点:

1.异步任务创建后,其自动发送HTTP请求,自动获取回复;

2.异步任务的调用代码只需要定时查询异步任务对象的状态,根据状态获取HTTP请求的结果。

以下给出了这种异步任务的例子:

cpp 复制代码
#ifndef HTTP_ASYNC_TASK_H
#define HTTP_ASYNC_TASK_H

#include <memory>
#include <thread>
#include <QString>
#include <QMutex>

class HttpAsyncTask
{
public:
    HttpAsyncTask(QString& urlStr, int timeout = 1000);
    HttpAsyncTask(const HttpAsyncTask&) = delete;
    HttpAsyncTask& operator=(const HttpAsyncTask&) = delete;
    virtual ~HttpAsyncTask();

    /** 任务执行完毕了吗?
    */
    bool IsOver();

    /** Get的结果
     */
    bool GetResult(bool& isSuccess, QString& val, QString& err);

protected:
    void _run();

private:
    QMutex    _mutex;
    QString    _url;
    int        _timeout;
    bool       _over;
    bool       _success;
    QString    _val;
    QString    _err;

    std::shared_ptr<std::thread> _ptrThread;

}; //HttpAsyncTask

#endif // HTTP_ASYNC_TASK_H
cpp 复制代码
#include <functional>
#include <QMutexLocker>
#include "HttpAsyncTask.h"

HttpAsyncTask::HttpAsyncTask(QString& urlStr, int timeout = 1000)
{
    _url = urlStr;
    _timeout = timeout;
    _over = false;
    _success = false;
    _val = "";
    _err = "";

    auto func = std::bind(&HttpAsyncTask::_run, this);
    _ptrThread = std::make_shared<std::thread>(func);
}

HttpAsyncTask::~HttpAsyncTask()
{
    _ptrThread->join();
    _ptrThread.reset();
}

void HttpAsyncTask::_run()
{
    QString urlStr = "";
    int timeout = 1000;

    {
        QMutexLocker lock(&_mutex);

        urlStr = _url;
        timeout = _timeout;
        _val = "";
        _err = "";
    }

    QString val = "";
    QString err = "";
    bool ret = HttpRequestSync(urlStr, val, err, timeout);
    if(ret)
    {
        QMutexLocker lock(&_mutex);
        
        _val = val;
        _err = "";
        _success = true;
        _over = true;
    }
    else
    {
        QMutexLocker lock(&_mutex);
        
        _val = "";
        _err = err;
        _success = false;
        _over = true;
     }    
}

bool HttpAsyncTask::IsOver()
{
    QMutexLocker lock(&_mutex);

    return _over;
}


bool HttpAsyncTask::GetResult(bool& isSuccess, QString& val, QString& err)
{
    QMutexLocker lock(&_mutex);

    if(!_readedOver)
        return false;

    isSuccess = _success;
    val = _val;
    err = _err;
    return true;
}
相关推荐
Goodbye16 小时前
大模型无状态架构:从 HTTP 协议到 Harness AI 工程的深度解析
http
Quz1 天前
QML Hello World 入门示例
qt
xcyxiner4 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner5 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner5 天前
DicomViewer (添加模型类)3
qt
xcyxiner6 天前
DicomViewer (目录调整) 2
qt
xcyxiner6 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
霜落长河7 天前
抛弃TCP改用UDP,HTTP3怎么了?
http
桥田智能8 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
之歆8 天前
现代 HTTP 客户端深度解析:Fetch 与 Axios
chrome·网络协议·http