C++Qt5学习笔记 2020-12-21(网络编程2:udp单播、广播、组播,HTTP下载网络文件)

44 篇文章 8 订阅

部分资料整理自Qt5.9 C++开发指南,代码和演示部分都是我修改和试验而来。

1、QUdpSocket实现UDP通信:

(1)通信概述:

UDPUser 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组播必须使用一个组播地址。组播地址是DIP地址,有特定的地址段。多播组可以是永久的也可以是临时的。多播组地址中,有一部分由官方分配,称为永久多播组。永久多播组保持不变的是它的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中的IP224.0.0.2,然后点击组播消息
在这里插入图片描述
发送成功了。除此之外,还可以试试改为224.0.0.3,然后就会发现并没有发送成功,因为224.0.0.3并不是这个多播组的成员。

2、基于HTTP协议的网络应用程序

(1)实现高层网络操作的类:

Qt网络模块提供一些实现OSI(开放式系统互联通信参考模型 Open System Interconnection Reference Model)7 层网络模型中高层的网络协议,如HTTPFTPSNMP等,这些类主要是QNetworkRequestQNetworkReplyQNetworkAccessManger

QNetworkRequest类通过一个URL地址发起网络协议请求,也保存网络请求的信息,目前支持HTTPFTP和局部文件URLs的下载或上传。
QNetworkAccessManager类用于协调网络操作。在QNetworkRequest发起一个网络请求后,QNetworkAccessManger类负责发送网络请求,创建网络响应。
QNetworkReply类表示网络请求的响应。由QNetworkAccessManager在发送一个网络请求后创建一个网络响应。QNetworkReply提供的信号finished()readyRead()downloadProgress()可以检测网络响应的执行情况,执行相应操作。
QNetworkReplyQIODevice的子类,所以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,然后解压就可以了。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值