Day28Qt实现http请求2022-01-06

Qt Network(三)http请求

介绍

1.network模块
首先,简单介绍一下network模块。Qt Network模块用来编写基于TCP/IP的网络程序,其中提供了较低层次的类,比如QTcpSocket、QTcpServer、QUdpSocket等,来表示低层次的网络概念;还有高层次的类,比如QNetworkRequest、QNetworkReply和QNetworkAccessManager,使用通用的协议来执行网络操作。Qt的网络模块是一个庞大的体系,还有网络代理类QNetworkProxy类,承载管理QNetworkConfigurationManager类、QNetworkSession类以及涉及通信安全的QSsl等。关于这些内容,敬请关注《QT积累——小项目》部分,将会陆续制作。
2.网络访问接口
网络访问接口是一组执行常见的网络操作的类的集合。该接口在特定的操作和协议(例如,http)上提供了一个抽象层,开发者只需要使用其提供的类、函数和信号即可完成操作,而不需要知道底层是如何实现的。网络请求是由QNetworkRequest类来表示,它也作为与请求有关的信息的容器(也就是一个集合了与“请求”相关属性及方法的类对象)。QNetworkAccessManager类用来协调网络操作,可以调度创建好的请求,并发射信号来报告进度。(也是一个类对象,表征的是网络操作)网络请求的应答使用QNetworkReply类表示,他会在请求调度完成时由QNetworkAccessManager创建。QNetworkReply提供的信号可以用来单独监视每一个应答,也就是这个类对象表征的应答的信息。
3.介绍相关术语
简单介绍与网络操作的相关术语,让各位清楚自己正在做的是什么事情。WWW既是万维网,网上好多的定义,有的说web服务器就是万维网,有的说互相链接的超文本组成的系统,百度百科中说,WWW (World Wide Web,万维网)是存储在Internet计算机中、数量巨大的文档的集合。抽象的东西似乎都没有准确的定义,但事实上万维网是一个具象的东西。我的理解是,符合以下特征就是万维网。首先数量巨大的文档的集合存储在WEB服务器中;其次,这些文档称为页面,它是一种超文本(Hypertext)信息,可以用于描述超媒体。文本、图形、视频、音频等多媒体,称为超媒体(Hypermedia);再次,Web上的信息是由彼此关联的文档组成的,而使其连接在一起的是超链接;
超文本(Hypertext)是由一个叫做网页浏览器(Web browser)的程序显示;最后,客户端使用http协议、https协议访问服务器,服务器能够响应这些请求,给出应答。符合这些特征的C/S模型就是万维网。
在万维网中,任何一个信息资源都有统一的并且在网上唯一的地址,这个地址就叫做URL,又称同一资源定位符。而HTTP协议是浏览器与WEB服务器交互遵从的协议,这个协议是基于TCP协议的,称为超文本传输协议。可以说,www是建立在http上完成通信的。互联网和万维网的区别更像是,互联网是基础,理论上可以在互联网中的任何两个终端都可以相互传输数据;万维网是应用,在互联网,一端是客户端,一端是服务器,由客户端向服务器发出请求(http协议),服务器做出应答的(超文本)的应用。类似的还有FTP应用,网上聊天应用等都不是万维网,但是都在用互联网。有关于http还有很多知识,例如报文格式,不同类型的请求等,更多网络知识,将在我的专栏《计算机网络》中分享更多积累与个人看法,供大家参考与讨论。

目标

本文将完成利用qt程序发出http请求,请求一个网络资源,并下载到本地。

效果

可以看到,请求了一张图片,jpg格式。进度条显示完毕之后,在本地目录中打开下载文件,如下,证明试验成功。

步骤

由HTTP客户端(qt程序)发起一个请求,建立一个到服务器制定端口的TCP连接(默认80端口);HTTP服务器在指定的端口监听客户端发送过来的请求,一旦收到请求,服务器端就会向客户端发回一个应答。在程序中,使用get()函数发出请求,返回一个QNetworkReply对象管理应答,当数据来临时,产生readyread信号;过程中出错会产生error信号;下载进度更新时会产生process信号;处理结束会产生finish信号。因此,发出请求,接收应答对象,关联相关信号槽,在槽内做处理即可。出错或者结束,打印告知用户,或者对话框;新数据来就往文件里面写;进度更新就去更新进度条。

总结与思考

1.通过supportedSchemes();方法查看QNetworkAccessManager类支持的协议,例如FTP,可以实现看看,在实践中理解http与ftp的异同,在网络协议模型中所处的位置,更好的理解计算机网络理论知识。
2.使用network模块,记得在pro文件中添加network模块。
3.我在实践中遇到了QNetworkReply::UnknownNetworkError,在网络中查询答案,未果,最终确定由于本地电脑加密软件的原因造成,因此当出现这个问题的时候,关闭防火墙,关闭杀毒软件,关闭与网络相关的应用进程,多从本地配置的角度考虑,因为qt对于network的支持,尤其是这种简单http请求的支持应该是比较完善了。
4.在与网络有关的程序的调试时,有一个抓包软件叫做wireshark,对理论理解大有帮助。wireshark下载地址
5.遇到错误qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed时,去openssl官网下载Win64 OpenSSL v3.0.2 Light工具。具体下载那个版本,应在程序中添加以下代码查询:

#include 
qDebug()<<QSslSocket::sslLibraryBuildVersionString();

可以看到我的程序中的版本。
在这里插入图片描述

如下图,安装过程中要注意有一个选项,要选拷贝到其他目录。安装好后在安装目录找到 libcrypto-1_1-x64.dll libssl-1_1-x64.dll 两个文件 ,然后粘贴到
qt安装目录X:\Qt\Qt5.15.2\5.15.2\msvc2019_64\bin目录再次运行,错误消失,完美解决。
Day28Qt实现http请求2022-01-06_第1张图片
6.By the way,OpenSSL是干什么的?在计算机网络上,OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份。这个包广泛被应用在互联网的网页服务器上。SSL是Secure Sockets Layer(安全套接层协议)的缩写,可以在Internet上提供秘密性传输。Netscape公司在推出第一个Web浏览器的同时,提出了SSL协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时实现支持。已经成为Internet上保密通讯的工业标准。

代码

.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    void Init();
    QNetworkAccessManager *manager;
    QNetworkReply *reply;
    QFile *myFile;
private slots:
    void doProcessReadyRead();
    void doProcessError(QNetworkReply::NetworkError);
    void doProcessFinished();
    void doProcessDownload(qint64,qint64);
    void on_getBtn_clicked();
};
#endif // WIDGET_H

.cpp文件

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    Init();
}
Widget::~Widget()
{
    delete ui;
}
void Widget::Init()
{
    //为全局变量分配空间,记得调用哦!
    manager = new QNetworkAccessManager(this);
    myFile = new QFile(this);
}
void Widget::doProcessReadyRead()
{
    //读取应答数据,并且写入文件中
    while(!reply->atEnd()){
        QByteArray ba = reply->readAll();
        myFile->write(ba);
    }
}
void Widget::doProcessError(QNetworkReply::NetworkError error)
{
    //打印请求过程中的错误,具体情况帮助手册有枚举
    qDebug()<<reply->errorString();
    switch ((int)error) {
    case QNetworkReply::ConnectionRefusedError:
            qDebug()<<"QNetworkReply::ConnectionRefusedError";
        break;
    case QNetworkReply::RemoteHostClosedError:
            qDebug()<<"QNetworkReply::RemoteHostClosedError";
    default:qDebug()<<error;
        break;
    }
}
void Widget::doProcessFinished()
{
    qDebug()<<"接收数据完毕!";
    myFile->close();//强制刷新
}
void Widget::doProcessDownload(qint64 a, qint64 b)
{
    //进度条的显示
    ui->progressBar->setMaximum(b);//all total
    ui->progressBar->setValue(a);//recv_total
}
void Widget::on_getBtn_clicked()
{
    QNetworkRequest req;
    req.setRawHeader(QByteArray("User-Agent"),QByteArray("MyOwnBrowser 1.0"));//设置http包的请求头
    QString url_str = ui->lineEdit->text();
    QUrl url;
    url.setUrl(url_str);//获取用户输入的url
    req.setUrl(url);
//  if(manager->networkAccessible() == QNetworkAccessManager::NotAccessible){
//     manager->setNetworkAccessible(QNetworkAccessManager::Accessible);
//  }
    reply = manager->get(req);//发送http get请求
    connect(reply,SIGNAL(readyRead()),this,SLOT(doProcessReadyRead()));//数据来临的信号,IO操作大多数都是这个信号
    connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(doProcessError(QNetworkReply::NetworkError)));
    connect(reply,SIGNAL(finished()),this,SLOT(doProcessFinished()));//传输(一次应答)完成
    //会将总文件长度,和当前文件传输长度反馈到参数中,正适合做进度条
    connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(doProcessDownload(qint64,qint64)));

    QStringList list = url_str.split("/");
    QString filename = list.at(list.length()-1);
    myFile->setFileName(filename);
    bool ret = myFile->open(QIODevice::WriteOnly|QIODevice::Truncate);//以重写的方式打开,在写入新的数据时会将原有
                                                                        //数据全部清除,游标设置在文件开头。
    ui->progressBar->setValue(0);
    ui->progressBar->setMinimum(0);
}

main文件

#include "widget.h"
#include 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

ui文件

Day28Qt实现http请求2022-01-06_第2张图片

你可能感兴趣的:(Qt积累——基础篇,http,网络,qt)