QT自带的HTTP请求类,如果直接使用会卡死界面,体验非常不好,于是我封装了一个http请求类,同时支持同步和异步的方式访问,最主要的好处是异步的时候使用了多线程,不卡主界面!
而使用同步的时候,虽然理论上也不卡主线程,但是实际测试界面仍然会卡死,原因可能和QEventloop有关,暂时不知道有什么解决办法。
直接上代码:
//author:autumoon
//联系QQ:4589968
//日期:2020-12-04
#ifndef HTTPREQUEST_H
#define HTTPREQUEST_H
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QThread>
#include <QEventLoop>
//请求方式,之所以使用宏定义是因为方便信号传输
#define HTTP_POST 1
#define HTTP_GET 2
#define HTTP_PUT 3
#define HTTP_DELETE 4
class HttpRequest : public QObject
{
Q_OBJECT
public:
explicit HttpRequest(QObject *parent = nullptr);
bool RequestHttpUrl(const QString& strUrl, int eHm = HTTP_POST, const QByteArray& baData = QByteArray(), int msec = 2000);
//判断主机是否在线
static bool IsHostOnline(QString strHostName, int nTimeoutmSeconds = 2000);
//判断是否通外网,能连接百度IP说明可以通外网
static bool IsWebOk(){return IsHostOnline("202.108.22.5", 2000);}
Q_SIGNALS:
void reply_data(bool bSuccess, const QByteArray& ba);
private:
static bool parseReplyData(QNetworkReply* reply, QByteArray& data);
static void setRequestHeader(QNetworkRequest &request)
{
request.setRawHeader("Content-Type", "charset='utf-8'");
request.setRawHeader("Content-Type", "application/json");
}
void sleep(int msec = 5000);
};
#endif // HTTPREQUEST_H
//启用线程调用下载
class requestWorker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString& strUrl, int eHm, const QByteArray& baData, int msec = 2000)
{
/* ... here is the expensive or blocking operation ... */
HttpRequest *hr = new HttpRequest(this);
connect(hr, SIGNAL(reply_data(bool, const QByteArray&)), this, SIGNAL(reply_data(bool, const QByteArray&)));
hr->RequestHttpUrl(strUrl, eHm, baData, msec);
}
Q_SIGNALS:
void reply_data(bool bSuccess, const QByteArray& ba);
};
class ThreadHttpRequest : public QObject
{
Q_OBJECT
QThread workerThread;
public:
ThreadHttpRequest()
{
requestWorker *worker = new requestWorker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &ThreadHttpRequest::request_data, worker, &requestWorker::doWork);
connect(worker, &requestWorker::reply_data, this, &ThreadHttpRequest::get_result);
connect(worker, &requestWorker::reply_data, this, &ThreadHttpRequest::reply_data);
workerThread.start();
}
~ThreadHttpRequest()
{
workerThread.quit();
workerThread.wait();
}
//异步,信号返回结果
void RequestHttpUrl(const QString& strUrl, int eHm, const QByteArray& baData, int msec = 2000)
{
emit request_data(strUrl, eHm, baData, msec);
}
//同步,直接返回结果
bool RequestHttpUrl(const QString& strUrl, int eHm, const QByteArray& baData, QByteArray& baRes, int msec = 2000)
{
QEventLoop el;
connect(this, SIGNAL(reply_data(bool, const QByteArray&)), &el, SLOT(quit()));
emit request_data(strUrl, eHm, baData, msec);
el.exec(QEventLoop::ExcludeUserInputEvents);
//获取结果
baRes = m_baRes;
return m_bRes;
}
Q_SIGNALS:
void reply_data(bool bSuccess, const QByteArray& ba);
void request_data(const QString& strUrl, int eHm, const QByteArray& baData, int msec);
private slots:
void get_result(bool bSuccess, const QByteArray& ba)
{
m_bRes = bSuccess;
m_baRes = ba;
}
private:
bool m_bRes;
QByteArray m_baRes;
};
实现文件:
//author:autumoon
//联系QQ:4589968
//日期:2020-12-04
#include "HttpRequest.h"
#include <QEventLoop>
#include <QTimer>
#include "JsonParser.h"
HttpRequest::HttpRequest(QObject *parent) : QObject(parent)
{
}
bool HttpRequest::RequestHttpUrl(const QString &strUrl, int eHm, const QByteArray &baData, int msec)
{
QNetworkRequest request(strUrl);
setRequestHeader(request);
QNetworkAccessManager* naManager = new QNetworkAccessManager(this);
QEventLoop eventloop;
QTimer timer;
timer.singleShot(msec, &eventloop, SLOT(quit()));
timer.start();
QNetworkReply* reply = nullptr;
switch (eHm)
{
case HTTP_POST:
reply = naManager->post(request, baData);
break;
case HTTP_PUT:
reply = naManager->put(request, baData);
break;
case HTTP_DELETE:
reply = naManager->deleteResource(request);
break;
case HTTP_GET:
default:
reply = naManager->get(request);
break;
}
QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
Q_ASSERT(conRet);
eventloop.exec(QEventLoop::ExcludeUserInputEvents);
QByteArray baReply;
if (timer.isActive() && parseReplyData(reply, baReply))
{
reply->deleteLater();
emit reply_data(true, baReply);
return true;
}
//超时,未知状态
disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
reply->abort();
reply->deleteLater();
emit reply_data(false, baReply);
return false;
}
bool HttpRequest::IsHostOnline(QString strHostName, int nTimeoutmSeconds)
{
QNetworkRequest request(strHostName);
setRequestHeader(request);
QNetworkAccessManager* naManager = new QNetworkAccessManager;
QEventLoop eventloop;
QTimer timer;
timer.singleShot(nTimeoutmSeconds, &eventloop, SLOT(quit()));
timer.start();
QNetworkReply* reply = naManager->get(request);
QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
Q_ASSERT(conRet);
eventloop.exec(QEventLoop::ExcludeUserInputEvents);
if (!timer.isActive())
{
//超时,未知状态
disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
reply->abort();
reply->deleteLater();
return false;
}
if (reply->error() != QNetworkReply::NoError)
{
reply->abort();
reply->deleteLater();
return false;
}
bool bRes = reply->readAll().length() > 0;
reply->abort();
reply->deleteLater();
return bRes;
}
bool HttpRequest::parseReplyData(QNetworkReply *reply, QByteArray &data)
{
if (reply == nullptr)
{
data = QString("Unbelievable! The reply pointer is nullptr!").toUtf8();
return false;
}
QString strResult;
// 获取http状态码
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if(statusCode.isValid())
strResult += "status code=" + statusCode.toString() + '\n';
QVariant reason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
if(reason.isValid())
strResult += "reason=" + reason.toString() + '\n';
QNetworkReply::NetworkError err = reply->error();
if(err != QNetworkReply::NoError)
{
strResult += "The err code is " + QString::number(err) + ";";
data = strResult.toUtf8();
return false;
}
else
{
//输出内容
data = reply->readAll();
}
return true;
}
void HttpRequest::sleep(int msec)
{
QEventLoop loop;//定义一个新的事件循环
QTimer::singleShot(msec, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数
loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}
欢迎交流与讨论。