文章目录
- 前言
-
- [1. 定义的接口](#1. 定义的接口)
- 2.connect信号槽
- [3. get](#3. get)
- [4. get 下载文件](#4. get 下载文件)
- [5. post](#5. post)
- 总结
前言
/*
1.请求报文:
请求报文是由客户端发送给服务器,用于请求特定资源或执行特定操作。它由以下几个部分组成:
请求行:描述了请求的方法、目标资源的路径和HTTP协议的版本,通常包含以下三个字段:
请求方法:指定了客户端希望服务器执行的操作,如GET、POST、PUT、DELETE等。 请求目标:表示客户端希望访问的资源路径,可以是绝对路径或相对路径。 协议版本:指定所使用的HTTP协议的版本,如HTTP/1.1。 请求头:包含了关于请求的附加信息,格式为键值对。常见的请求头字段包括: Host:指定请求的目标主机。 User-Agent:标识发送请求的客户端应用程序。 Content-Type:指定请求正文的类型。 请求正文(可选):包含客户端发送给服务器的数据,通常在使用POST等方法时使用。
2.响应报文:
响应报文是服务器对客户端请求的回应,包含了所请求资源的数据或执行结果。它由以下几个部分组成:
状态行:描述了响应的状态,包含以下三个字段:
协议版本:指定所使用的HTTP协议的版本,如HTTP/1.1。 状态码:表示服务器对请求的处理结果,如200表示成功,404表示资源未找到。 状态信息:对状态码进行简短的解释说明。 响应头:包含了关于响应的附加信息,格式为键值对。常见的响应头字段包括: Content-Type:指定响应正文的类型。 Content-Length:指定响应正文的长度。 Set-Cookie:在响应中设置Cookie。 响应正文:包含了服务器返回给客户端的数据,可以是HTML、JSON、文件等。 请求报文和响应报文的结构化文本格式使得客户端和服务器能够互相理解并进行有效的通信。 它们是HTTP通信的基础,用于传递请求和响应的相关信息 ****************************************************** text/html 表示数据格式是 HTML text/css 表示数据格式是 CSS application/javascript 表示数据各式是 JavaScript application/json 表示数据格式是 JSON ******************************************************
*/
1. 定义的接口
- public公共接口,用作单线程
- public slots公共槽函数,支持类对象调用,在哪个线程调用即在哪个线程运行
- signals 信号,通过调用信号的方式在其对应槽函数线程创建事件执行,用于多线程
cpp
public:
bool get(QString url, QString &data, int timeout = 20000);
bool post(QString url, QString &data, QByteArray jsonData, int timeout = 20000);
bool getDownload(QString url, QString filePath, int timeout = 20000);
signals:
void sgnGet(QString url, int timeout = 20000);
void sgnPost(QString url,QByteArray jsonData, int timeout = 20000);
void sgnGetDownload(QString url, QString filePath, int timeout = 20000);
void Progress(qint64, qint64);
void finished(QString data, bool result);
public slots:
void gets(QString url, int timeout = 20000);
void posts(QString url,QByteArray jsonData, int timeout = 20000);
void getDownloads(QString url, QString filePath, int timeout = 20000);
2.connect信号槽
cpp
connect(this, &Http::sgnGet, this, &Http::gets);
connect(this, &Http::sgnGetDownload, this, &Http::getDownloads);
connect(this, &Http::sgnPost, this, &Http::posts);
3. get
cpp
//! 通过信号槽的方式调动
void Http::gets(QString url, int timeout)
{
QString data = "";
bool rt = this->get(url, data, timeout);
emit finished(data, rt);
}
cpp
//! get请求数据
bool Http::get(QString url, QString& data, int timeout)
{
qDebug()<<"Http QThread::currentThread() = "<<QThread::currentThread();
int repeatSend = 0;
// 三次请求失败结束请求数据
while(!repeatGet(url, data, timeout))
{
if(++repeatSend >= 3) return false;
}
return true;
}
cpp
//!
bool Http::repeatGet(QString& url, QString& data, int& timeout)
{
// 建立事件循环
QEventLoop loop;
//设置发送请求所需的信息
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/x-www-form-urlencoded"));
//管理网络请求和响应
QNetworkAccessManager manager;
connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
// 超时检测
QTimer timer;
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
// 处理网络请求的响应数据
QNetworkReply* pReply = manager.get(request);
connect(pReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(pReply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
// 超时检测定时器启动
timer.start((timeout > 0) ? timeout : 2000);
//执行事件循环,直到退出循环再执行后面代码
loop.exec();
//! 退出事件循环,判断定时器是否触发,触发即超时
if(!timer.isActive())
{
pReply->deleteLater();
return false;
}
//! 未超时停止定时器
timer.stop();
//
QNetworkReply::NetworkError err = pReply->error();
if(err != QNetworkReply::NoError)
{
// 检测状态码
int statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<"get error, statusCode = "<<statusCode;
return false;
}
//
data = QString::fromUtf8(pReply->readAll());
pReply->deleteLater();
return true;
}
执行代码:
cpp
QString data = "";
qDebug()<<"----------------------------------";
qDebug()<< "MainWindow QThread::currentThread() = "<< QThread::currentThread();
qDebug()<<data.size();
data.clear();
qDebug()<<"--------------- 1 ----------------";
http->get(QString("http://www.baidu.com"),data, 2000);
qDebug()<<data.size();
data.clear();
qDebug()<<"--------------- 2 ----------------";
http->gets(QString("http://www.baidu.com"), 2000);
qDebug()<<data.size();
data.clear();
qDebug()<<"--------------- 3 ----------------";
emit http->sgnGet(QString("http://www.baidu.com"), 2000);
qDebug()<<data.size();
qDebug()<<"----------------------------------";
// 保存HTTP响应内容
// 组装保存的文件名 文件名格式: 路径/年_月_日 小时_分_秒 httpfile.html
QDateTime current_date_time =QDateTime::currentDateTime();
QString current_date =current_date_time.toString("yyyy_MM_dd hh_mm_ss");
QString filePath = ".";
QString fileName = filePath + '/' + current_date + " httpfile" + ".html";
QFile file(fileName);
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)){
//qDebug() << "file open error!";
return ;
}
QTextStream out(&file);
out.setCodec("UTF-8");
out<<data;
file.close();
data.clear();
通过信号槽是多线程且异步的
4. get 下载文件
cpp
void Http::getDownloads(QString url, QString filePath, int timeout)
{
bool rt = this->getDownload(url, filePath, timeout);
emit finished(filePath, rt);
}
cpp
bool Http::getDownload(QString url, QString filePath, int timeout)
{
qDebug()<<"Http QThread::currentThread() = "<<QThread::currentThread();
int repeatSend = 0;
//
while(!repeatGetDownload(url, filePath, timeout))
{
if(++repeatSend >= 3) return false;
}
return true;
}
cpp
bool Http::repeatGetDownload(QString& url, const QString& filePath, const int& timeout)
{
if(!pFile.isOpen()) pFile.setFileName(filePath);
//
QEventLoop loop;
//
QNetworkRequest request;
request.setUrl(QUrl(url));
//
QNetworkAccessManager manager;
connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
//
QTimer timer;
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
//
QNetworkReply* pReply = manager.get(request);
connect(pReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(pReply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
connect(pReply, &QNetworkReply::readyRead, &loop, &QEventLoop::quit);
//
timer.start((timeout > 0) ? timeout : 20000);
loop.exec(QEventLoop::ExcludeSocketNotifiers);
disconnect(pReply, &QNetworkReply::readyRead, &loop, &QEventLoop::quit);
//! 超时
if(!timer.isActive())
{
pReply->deleteLater();
return false;
}else{
timer.stop();
}
QNetworkReply::NetworkError err = pReply->error();
if(err != QNetworkReply::NoError)
{
// 检测状态码
int statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<"get error, statusCode = "<<statusCode;
//! 重定向
const QVariant redirectionTarget = pReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(!redirectionTarget.isNull())
{
QUrl redirectedUrl = redirectionTarget.toUrl();
url = redirectedUrl.toString();
}
return false;
}
connect(pReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(LoadProgress(qint64,qint64)));
connect(pReply, &QNetworkReply::readyRead, this, &Http::readSave);
loop.exec();
return true;
}
cpp
void Http::readSave()
{
QNetworkReply* pReply = (QNetworkReply*)sender();
if(!pFile.isOpen())
{
//! save file
if(!pFile.open(QIODevice::WriteOnly))
{
qDebug() << pFile.errorString();
}
}
pFile.write(pReply->readAll());
}
cpp
void Http::LoadProgress(qint64 recved, qint64 total)
{
QNetworkReply* pReply = (QNetworkReply*)sender();
if(recved >= total)
{
pFile.close();
pReply->deleteLater();
}
}
执行代码
cpp
QString url = "https://1.as.dl.wireshark.org/win64/Wireshark-win64-4.0.10.exe";
url = "https://enigmaprotector.com/assets/files/enigma_en_demo.exe";
qDebug()<<"----------------------------------";
qDebug()<< "MainWindow QThread::currentThread() = "<< QThread::currentThread();
qDebug()<<"--------------- 1 ----------------";
QString path = "./enigma1.exe";
//http->getDownload(url, path, 4000);
qDebug()<<"--------------- 2 ----------------";
path = "./enigma2.exe";
//http->getDownloads(url, path, 4000);
qDebug()<<"--------------- 3 ----------------";
path = "./enigma3.exe";
emit http->sgnGetDownload(url, path, 4000);
qDebug()<<"----------------------------------";
5. post
没测试过
cpp
void Http::posts(QString url,QByteArray jsonData, int timeout)
{
QString data;
bool rt = this->post(url, data, jsonData, timeout);
emit finished(data, rt);
}
cpp
//!
bool Http::post(QString url, QString& data, QByteArray jsonData, int timeout)
{
int repeatSend = 0;
//
while(!repeatPost(url, data, jsonData, timeout))
{
++repeatSend;
if(++repeatSend >= 3)
return false;
}
return true;
}
//
bool Http::repeatPost(QString& url, QString& data, QByteArray& jsonData, int& timeout)
{
//
QEventLoop loop;
//
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml;charset=UTF-8");
//
QNetworkAccessManager manager;
connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
//
QTimer timer;
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
//
QNetworkReply* pReply = manager.post(request, jsonData);
connect(pReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(pReply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
//
timer.start((timeout > 0) ? timeout : 2000);
loop.exec();
//! 超时
if(!timer.isActive())
{
pReply->deleteLater();
return false;
}
//!
timer.stop();
//
QNetworkReply::NetworkError err = pReply->error();
if(err != QNetworkReply::NoError)
{
//! 检测状态码
int statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<"get error, statusCode = "<<statusCode;
//! 重定向
const QVariant redirectionTarget = pReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(!redirectionTarget.isNull())
{
QUrl redirectedUrl = redirectionTarget.toUrl();
url = redirectedUrl.toString();
}
return false;
}
//!
data = QString::fromUtf8(pReply->readAll());
pReply->deleteLater();
return true;
}
总结
学啥记啥,好记性不如烂笔头!