部分资料整理自Qt5.9 C++开发指南
,代码和演示部分都是我修改和试验而来。
1、QUdpSocket实现UDP通信:
(1)通信概述:
UDP
(User Datagram Protocol
,用户数据报协议)是轻量的、不可靠的、面向数据报(datagram)、无连接的协议,它可以用于对可靠性要求不高的场合。与TCP
通信不同,两个程序之间进行UDP
通信无需先建立持久的socket
连接,UDP
每次发送数据报都需要指定目标地址和端口。
QUdpSocket
类用于实现UDP
通信,它从QAbstractSocket
类来继承,因而与QTcpSocket
共享大部分的接口函数。主要区别是QUdpSocket
以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram()
,数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP
地址和端口等信息。
要进行UDP
数据接收,要用QUdpSocket::bind()
函数先绑定一个端口,用于接收传入的数据报。当有数据报传入时会发射readyRead()
信号,使用readDatagram()
函数来读取接收到的数据报。
UDP
消息传送有单播、广播、组播三种模式:
单播(unicast
)模式: 一个UDP客户端发出的数据报之发送到另一个指定地址和端口的UDP
客户端,是一对一的数据传输。
广播(broadcast
)模式: 一个UDP
客户端发出的数据报,在同一网络范围内其他所有的UDP
客户端都可以收到。QUdpSocket
支持IPv4
广播。广播经常用于实现网络发现的协议。要获取广播数据只需在数据报中指定接收端地址为QHostAddress::Broadcast
,一般的广播地址是255.255.255.255
。
组播(multicast
)模式: 也称为多播。UDP客户端加入到另一个组播IP地址指定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似于QQ群的功能。QUdpSocket::joinMulticastGroup()
函数实现加入多播组的功能,加入多播组后,UDP数据的收发与正常的UDP数据收发方法一样。
使用广播和多播模式,UDP
可以实现一些比较灵活的通信功能,而TCP
通信只有单播模式,没有广播和多播模式。所以,UDP
通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP
通信的。
QUdpSocket
类从QAbstractSocket
继承而来,但是又定义了较多新的功能函数用于实现UDP
特有的一些功能,如数据报读写和多播通信功能。QUdpSocket
没有定义新的信号。
QUdpSocket的主要功能函数表:
bool bind(quint16 port=0) //为UDP通信绑定一个端口
qint64 writeDatagram(QByteArray &datagram,QHostAddress &host,quint16 port) //向目标地址和端口的UDP客户端发送数据报,返回成功发送的字节数
bool hasPendingDatagrams() //当至少有一个数据报需要读取时,返回true
qint64 pendingingDatagramSize() //返回第一个待读取的数据报的大小
qint64 readDatagram(char *data,qint64 maxSize) //读取一个数据报,返回成功读取的数据报的字节数
bool joinMulticastGroup(QHostAddress &groupAddress) //加入一个多播组
bool leaveMulticastGroup(QHostAddress &groupAddress) //离开一个多播组
在单播、广播和多播模式下,UDP程序都是对等的,不像TCP通信那样分为客户端和服务器端。多播和广播的实现方式基本相同,只是数据报的目标IP地址设置不同,多播模式需要加入多播组,实现方式有较大差异。
(2)UDP通信代码1(单播和广播):
新建两个项目,分别是数据接收端udp_server_test
,和数据发送端udp_client_test
,下面就直接挂代码了。
数据接收端项目:
udp_server_test.pro
中引入network
:
QT += network
ui
设置:
mainwindow.h
中:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QUdpSocket *udpSocket;
private:
Ui::MainWindow *ui;
public slots:
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onSocketReadyRead(); //读取socket传入数据
void on_actStart_triggered(); //开始监听
void on_actStop_triggered(); //结束监听
};
#endif // MAINWINDOW_H
mainwindow.cpp
中:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle(u8"UDP接收端");
ui->label_2->setText(u8"UDP接收端");
udpSocket = new QUdpSocket(this); //初始化udpsocket到this
//连接状态变化时触发槽函数
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
//初始状态:未连接
onSocketStateChange(udpSocket->state());
//读取到数据时触发的槽函数
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
//开始监听按钮
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_actStart_triggered()));
//停止监听按钮
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(on_actStop_triggered()));
ui->pushButton_2->setEnabled(false);
}
//绑定端口监听
void MainWindow::on_actStart_triggered()
{
//绑定本地端口进行监听,绑定成功以后,连接状态会变成BoundState
if (udpSocket->bind(8888))
{
ui->label_4->setText(u8"绑定端口成功!");
ui->pushButton->setEnabled(false);
ui->pushButton_2->setEnabled(true);
}
}
//解除端口绑定和监听
void MainWindow::on_actStop_triggered()
{
udpSocket->abort(); //解除绑定,解绑成功后,连接状态会变成UnconnectedState
ui->label_4->setText(u8"已结束监听!");
ui->pushButton->setEnabled(true);
ui->pushButton_2->setEnabled(false);
}
//接收到数据时,触发的槽函数
void MainWindow::onSocketReadyRead()
{
while (udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);
QString str = datagram.data();
QString peer = "[From"+peerAddr.toString()+":"+QString::number(peerPort)+"]";
ui->label_3->setText(peer+str);
}
}
//连接状态变换槽函数
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
switch(socketState) //连接状态
{
case QAbstractSocket::UnconnectedState: //未连接
ui->label->setText(u8"Socket 状态:UnconnectedState"); break;
case QAbstractSocket::HostLookupState:
ui->label->setText(u8"Socket 状态:HostLookupState"); break;
case QAbstractSocket::ConnectedState:
ui->label->setText(u8"Socket 状态:ConnectedState"); break;
case QAbstractSocket::BoundState:
ui->label->setText(u8"Socket 状态:BoundState"); break;
case QAbstractSocket::ClosingState:
ui->label->setText(u8"Socket 状态:ClosingState"); break;
case QAbstractSocket::ListeningState:
ui->label->setText(u8"Socket 状态:ListeningState");
case QAbstractSocket::ConnectingState:
ui->label->setText(u8"Socket 状态:ConnectingState"); break;
}
}
MainWindow::~MainWindow()
{
delete ui;
}
数据发送端项目:
udp_client_test.pro
中引入network
:
QT += network
ui
设置:
mainwindow.h
中:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QUdpSocket *udpSocket;
private:
Ui::MainWindow *ui;
public slots:
void send_mess();
void broadcast_mes();
};
#endif // MAINWINDOW_H
mainwindow.cpp
中:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle(u8"UDP发送端");
ui->label->setText(u8"UDP发送端");
udpSocket = new QUdpSocket(this); //初始化udpsocket到this
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(send_mess()));
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(broadcast_mes()));
}
//发送消息
void MainWindow::send_mess()
{
QString targetIP = "192.168.1.5"; //指定IP发送
QHostAddress targetAddr(targetIP);
QString msg = u8"你好,我是客户端,我在单播消息";
QByteArray str = msg.toUtf8();
udpSocket->writeDatagram(str,targetAddr,8888); //发出数据报
ui->label_2->setText(u8"已发送消息"+msg);
}
//广播消息
void MainWindow::broadcast_mes()
{
//不需要写IP,UDP广播会发送给同一个子网中的所有ip
QString msg = u8"你好,我是客户端,我在广播消息";
QByteArray str = msg.toUtf8();
udpSocket->writeDatagram(str,QHostAddress::Broadcast,8888); //同一子网下所有IP广播数据报
ui->label_2->setText(u8"已发送消息"+msg);
}
MainWindow::~MainWindow()
{
delete ui;
}
运行结果:
运行接收端程序,点击监听按钮:
运行发送端程序,点击发送消息按钮进行单播通信:
点击广播消息按钮进行广播:
(3)UDP通信代码2(组播):
UDP组播是主机之间“一对一组”的通信模式,当多个客户端加入由一个组播地址定义的多播组之后,客户端向组播地址和端口发送的UDP数据报,组内成员都可以接收到,其功能类似于QQ群。
组播报文的目的地址使用D类IP地址,D类IP地址不能出现在IP报文的源IP地址字段。用同一个IP多播地址接收多播数据报的所有主机构成了一个组,称为多播组(或组播组)。所有的信息接收者都加入到一个组内,并且一旦加入之后,流向组地址的数据报立即开始向接收者传输,组中的所有成员都能接收到数据报。组中的成员是动态的,主机可以任何时间加入和离开组。
所以,采用UDP
组播必须使用一个组播地址。组播地址是D
类IP
地址,有特定的地址段。多播组可以是永久的也可以是临时的。多播组地址中,有一部分由官方分配,称为永久多播组。永久多播组保持不变的是它的IP
地址,组中的成员构成可以发生变化。永久多播组中成员的数量可以是任意的,甚至可以为0。那些没有被保留下来的永久多播组使用的IP组播地址,可以被临时多播组利用。关于组播IP地址,有如下的一些约定:
【1】 224.0.0.0
~224.0.0.255
为预留的组播地址(永久组地址),地址224.0.0.0
保留不做分配,其他地址供路由器协议使用。
【2】224.0.1.0
~224.0.1.255
是公用组播地址,可用于Internet;
【3】239.0.0.0
~239.255.255.255
为本地管理组播地址,仅在特定的本地范围内有效。
所以,若是在家或办公室局域网内测试UDP组播功能,可以使用的组播地址范围是239.0.0.0
~239.255.255.255
。
QUdpSocket
支持UDP
组播,joinMulticastGroup()
函数使主机加入一个多播组,leaveMulticastGroup()
函数使主机离开一个多播组,UDP
组播的特点是使用组播地址,其他端口绑定、数据报收发等功能的实现与单播UDP
完全相同。
组播通信:
数据报生存周期:
//MulticastTtlOption是UDP组播的数据报的生存周期,数据报每跨1个路由会减1。
//缺省值为1,表示多播数据报只能在同一路由下的局域网内传播。
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
绑定端口:
//绑定端口。UDP组播中,IP为QHostAddress::AnyIPv4
udpSocket->bind(QHostAddress::AnyIPv4,8888,QUdpSocket::ShareAddress)
IP加入多播组(这里不能使用之前的192.168.1.5
):
QString IP = "224.0.0.1"; //组播地址
groupAddress = QHostAddress(IP);
udpSocket->joinMulticastGroup(groupAddress); //加入组播
QString IP2 = "224.0.0.2"; //组播地址
groupAddress2 = QHostAddress(IP2);
udpSocket->joinMulticastGroup(groupAddress2); //加入组播
退出组播:
udpSocket ->leaveMulticastGroup(groupAddress); //退出组播
udpSocket ->leaveMulticastGroup(groupAddress2); //退出组播
接收数据:
udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);
发送数据报:
udpSocket->writeDatagram(str,targetAddr,8888); //发出数据报
代码演示:
为了实现双端互相组播通信,则两端都需要加入相应的代码,下面以单向的通信为例放入代码。
直接放代码:
接收端
ui
:
mainwindow.h
:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QUdpSocket *udpSocket;
QHostAddress groupAddress;
QHostAddress groupAddress2;
private:
Ui::MainWindow *ui;
public slots:
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onSocketReadyRead(); //读取socket传入数据
void on_actStart_triggered(); //开始监听
void on_actStop_triggered(); //结束监听
void clear_text();
};
#endif // MAINWINDOW_H
mainwindow.cpp
:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle(u8"UDP接收端");
ui->label_2->setText(u8"UDP接收端");
udpSocket = new QUdpSocket(this); //初始化udpsocket到this
//MulticastTtlOption是UDP组播的数据报的生存周期,数据报每跨1个路由会减1。
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
//连接状态变化时触发槽函数
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
//初始状态:未连接
onSocketStateChange(udpSocket->state());
//读取到数据时触发的槽函数
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
//开始监听按钮
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_actStart_triggered()));
//停止监听按钮
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(on_actStop_triggered()));
//数据清空
connect(ui->pushButton_3,SIGNAL(clicked()),this,SLOT(clear_text()));
ui->pushButton_2->setEnabled(false);
}
//绑定端口监听
void MainWindow::on_actStart_triggered()
{
//绑定本地端口进行监听,绑定成功以后,连接状态会变成BoundState
if (udpSocket->bind(QHostAddress::AnyIPv4,8888,QUdpSocket::ShareAddress))
{
QString IP = "224.0.0.1"; //组播地址
groupAddress = QHostAddress(IP);
udpSocket->joinMulticastGroup(groupAddress); //加入组播
QString IP2 = "224.0.0.2"; //组播地址
groupAddress2 = QHostAddress(IP2);
udpSocket->joinMulticastGroup(groupAddress2); //加入组播
ui->label_4->setText(u8"绑定端口成功!");
ui->pushButton->setEnabled(false);
ui->pushButton_2->setEnabled(true);
}
}
//解除端口绑定和监听
void MainWindow::on_actStop_triggered()
{
udpSocket ->leaveMulticastGroup(groupAddress); //退出组播
udpSocket ->leaveMulticastGroup(groupAddress2); //退出组播
udpSocket->abort(); //解除绑定,解绑成功后,连接状态会变成UnconnectedState
ui->label_4->setText(u8"已结束监听!");
ui->pushButton->setEnabled(true);
ui->pushButton_2->setEnabled(false);
}
//接收到数据时,触发的槽函数
void MainWindow::onSocketReadyRead()
{
while (udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);
QString str = datagram.data();
QString peer = "[From"+peerAddr.toString()+":"+QString::number(peerPort)+"]";
ui->label_3->setText(peer+str);
}
}
//清空label
void MainWindow::clear_text()
{
ui->label_3->setText("");
}
//连接状态变换槽函数
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
switch(socketState) //连接状态
{
case QAbstractSocket::UnconnectedState: //未连接
ui->label->setText(u8"Socket 状态:UnconnectedState"); break;
case QAbstractSocket::HostLookupState:
ui->label->setText(u8"Socket 状态:HostLookupState"); break;
case QAbstractSocket::ConnectedState:
ui->label->setText(u8"Socket 状态:ConnectedState"); break;
case QAbstractSocket::BoundState:
ui->label->setText(u8"Socket 状态:BoundState"); break;
case QAbstractSocket::ClosingState:
ui->label->setText(u8"Socket 状态:ClosingState"); break;
case QAbstractSocket::ListeningState:
ui->label->setText(u8"Socket 状态:ListeningState");
case QAbstractSocket::ConnectingState:
ui->label->setText(u8"Socket 状态:ConnectingState"); break;
}
}
MainWindow::~MainWindow()
{
delete ui;
}
发送端
ui
:
mainwindow.h
:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QUdpSocket *udpSocket;
private:
Ui::MainWindow *ui;
public slots:
void send_mess();
void broadcast_mes();
};
#endif // MAINWINDOW_H
mainwindow.cpp
:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle(u8"UDP发送端");
ui->label->setText(u8"UDP发送端");
udpSocket = new QUdpSocket(this); //初始化udpsocket到this
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(send_mess()));
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(broadcast_mes()));
}
//发送消息
void MainWindow::send_mess()
{
QString targetIP = ui->lineEdit->text(); //向LineEdit中输入的IP发送消息
QHostAddress targetAddr(targetIP);
QString msg = u8"你好,我是客户端,我在组播消息";
QByteArray str = msg.toUtf8();
udpSocket->writeDatagram(str,targetAddr,8888); //发出数据报
ui->label_2->setText(u8"已发送消息"+msg+u8"\n;消息接收的ip地址为"+targetIP);
}
//广播消息
void MainWindow::broadcast_mes()
{
//不需要写IP,UDP广播会发送给同一个子网中的所有ip
QString msg = u8"你好,我是客户端,我在广播消息";
QByteArray str = msg.toUtf8();
udpSocket->writeDatagram(str,QHostAddress::Broadcast,8888); //同一子网下所有IP广播数据报
ui->label_2->setText(u8"已发送消息"+msg);
}
MainWindow::~MainWindow()
{
delete ui;
}
运行结果:
首先,运行接收端和发送端程序,并点击接收端监听按钮,开始监听:
在lineEdit
中输入:224.0.0.1
,并点击组播消息
。
点击数据清空
,将label
接收到的数据清空,这一步就不演示了。然后修改lineEdit
中的IP
为224.0.0.2
,然后点击组播消息
。
发送成功了。除此之外,还可以试试改为224.0.0.3
,然后就会发现并没有发送成功,因为224.0.0.3
并不是这个多播组的成员。
2、基于HTTP协议的网络应用程序
(1)实现高层网络操作的类:
Qt
网络模块提供一些实现OSI(开放式系统互联通信参考模型 Open System Interconnection Reference Model)
7 层网络模型中高层的网络协议,如HTTP
、FTP
、SNMP
等,这些类主要是QNetworkRequest
、QNetworkReply
和QNetworkAccessManger
。
QNetworkRequest
类通过一个URL
地址发起网络协议请求,也保存网络请求的信息,目前支持HTTP
、FTP
和局部文件URLs
的下载或上传。
QNetworkAccessManager
类用于协调网络操作。在QNetworkRequest
发起一个网络请求后,QNetworkAccessManger
类负责发送网络请求,创建网络响应。
QNetworkReply
类表示网络请求的响应。由QNetworkAccessManager
在发送一个网络请求后创建一个网络响应。QNetworkReply
提供的信号finished()
、readyRead()
和downloadProgress()
可以检测网络响应的执行情况,执行相应操作。
QNetworkReply
是QIODevice
的子类,所以QNetworkReply
支持流读写功能,也支持异步或同步工作模式。
(2)下载文件:
从网址接收数据:
reply = networkManger.get(QNetworkRequest(newUrl));
读取下载的数据,写入QFile
:
downloadedFile->write(reply->readAll());
网络响应结束:
QFileInfo fileInfo;
fileInfo.setFile(downloadedFile->fileName());
downloadedFile->close();
delete downloadedFile;
downloadedFile = Q_NULLPTR;
reply->deleteLater();
reply = Q_NULLPTR;
(3)下载文件代码:
ui
:
mainwindow.h
:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QFile>
#include <QNetworkReply>
#include <QprogressBar>
#include <QFileInfo>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QNetworkAccessManager networkManger; //网络管理
QNetworkReply *reply; //网络响应
QFile *downloadedFile; //下载保存的临时文件
private slots:
void on_finished();
void on_readyRead();
void on_downloadProgress(qint64 bytesRead,qint64 totalBytes);
void clicked_download_file();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(clicked_download_file()));
ui->progressBar->setValue(0);
}
void MainWindow::clicked_download_file()
{
ui->pushButton->setEnabled(false);
QString urlSpec = "http://down.uu234w.com/txt/%E6%96%97%E7%BD%97%E5%A4%A7%E9%99%86IV%E7%BB%88%E6%9E%81%E6%96%97%E7%BD%97.txt"; //文件网址
QUrl newUrl = QUrl::fromUserInput(urlSpec);
QString tempDir = "E:/jcy/http_test/"; //下载目录
QString fullFileName = tempDir+newUrl.fileName();
downloadedFile = new QFile(fullFileName);
if(!downloadedFile->open(QIODevice::WriteOnly)){qDebug()<<"临时文件打开错误";}
reply = networkManger.get(QNetworkRequest(newUrl)); //网址接收数据
connect(reply,SIGNAL(finished()),this,SLOT(on_finished()));
connect(reply,SIGNAL(readyRead()),this,SLOT(on_readyRead()));
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this
,SLOT(on_downloadProgress(qint64,qint64)));
}
void MainWindow::on_readyRead()
{ //读取下载的数据
downloadedFile->write(reply->readAll());
}
void MainWindow::on_downloadProgress(qint64 bytesRead,qint64 totalBytes)
{ //下载进程
ui->progressBar->setMaximum(totalBytes);
ui->progressBar->setValue(bytesRead);
}
void MainWindow::on_finished()
{ //网络响应结束
QFileInfo fileInfo;
fileInfo.setFile(downloadedFile->fileName());
downloadedFile->close();
delete downloadedFile;
downloadedFile = Q_NULLPTR;
reply->deleteLater();
reply = Q_NULLPTR;
ui->pushButton->setEnabled(true);
}
MainWindow::~MainWindow()
{
delete ui;
}
运行结果:
这样就完成下载啦,只不过下载下来的文件打开来会是乱码,把后缀改成.rar
,然后解压就可以了。