在QT中,要执行HTTP请求,获取HTTP服务器的响应数据,需要启用network模块:
QT += network
QT的network模块主要有以下四个类:
1.QNetworkAccessManager: 该类的主要功能是处理网络请求和回复,管理网络接口,以及处理多线程网络操作;
2.QNetworkRequest:该类主要用于主要用于创建和管理HTTP请求;
3.QUrl:该类用于处理URL,提供了丰富的功能来解析、构造、操作URL;
4.QNetworkReplay:该类用于处理网络请求的响应。
1.常规异步接口
QT文档给出了使用这几个类进行HTTP请求的标准用法:
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.既然是同步接口访问可能发送阻塞的网络资源,就要加上超时参数。
以下给出了同步函数的例子:
#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请求的结果。
以下给出了这种异步任务的例子:
#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
#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;
}