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;
}
相关推荐
小短腿的代码世界21 小时前
Qt Bluetooth源码深度解析:从HCI协议到跨平台API的完整架构
开发语言·qt·架构
小短腿的代码世界1 天前
Qt WebEngine多进程架构与IPC通信:从Chromium多进程到Qt信号槽融合
qt·架构·系统架构
尘中远1 天前
【Qwt 7.0 系列】快速入门与核心新特性概览 —— 更现代的 Qt 数据可视化库
qt·数据可视化·绘图·qwt·科学绘图
冰暮流星1 天前
flask之http请求方法
网络·网络协议·http
初阳7851 天前
【Qt】系统相关(3)——多线程
qt
初阳7851 天前
【Qt】系统相关(2)——文件
开发语言·qt
尘中远2 天前
【Qwt 7.0 系列】坐标轴与刻度系统 —— 刻度引擎、网格、图例与刻度朝内
qt·数据可视化·qcustomplot·qwt·工业软件·科学绘图
sycmancia2 天前
Qt——多线程间的互斥
开发语言·qt
AlfredZhao2 天前
Linux 主机防火墙如何同时开启 80 和 443?
http·https·firewall
从零开始的代码生活_2 天前
NAT、代理服务与内网穿透详解
linux·服务器·网络·c++·http·智能路由器