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;
}
相关推荐
Graceful_scenery1 小时前
https双向认证
服务器·网络·网络协议·http·https
人才程序员11 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
学习BigData11 小时前
【使用PyQt5和YOLOv11开发电脑屏幕区域的实时分类GUI】——选择检测区域
qt·yolo·分类
njnu@liyong12 小时前
图解HTTP-HTTP状态码
网络协议·计算机网络·http
代码洁癖症患者15 小时前
HTTP请求的奇幻旅程:从发起至响应的全方位探索
网络·网络协议·http
寻找沙漠的人18 小时前
HTTP—02
网络·网络协议·http
范紫涵-19期-工职大20 小时前
前端HTTP协议传输以及背后的原理总结
网络·网络协议·http
yerennuo21 小时前
FFmpeg库之ffmpeg
qt·ffmpeg
冷眼看人间恩怨21 小时前
【Qt笔记】QComboBox控件详解
c++·笔记·qt
阿松のblog21 小时前
pyQt5实现页面切换操作
开发语言·qt