QNetworkAccessManager
看了两天的Qt网络编程,其实主要就是看了看QNetworkAccessManager、QNetworkRequest和 QNetworkReply这三个类的主要内容。在之前,Qt网络编程主要是使用QHttp等类,但是现在在帮助手册中这些类已经标记为过时的,所以,现 在用Qt编写网络程序最好还是使用上面的三个类,之前也说过,对于一个应用程序来说,一个QNetworkAccessManager已经足够了。不知道 你有没有通过名字看出这三个类的联系呢?这里再贴一下三个类的官方说明,加强印象,也有助于大家对照接下来的示例看。
QNetworkAccessManager
QNetworkRequest
QNetworkReply
Allows the application to send network requests and receive replie
Holds a request to be sent with QNetworkAccessManager
Contains the data and headers for a request sent with QNetworkAccessManager
个人感觉三者关系可简单理解如下:
HTTP即超文本传输协议,它是一种文件传输协议。这一节中我们将讲解如何利用HTTP从网站上下载文件。
上一节中我们已经提到过了,现在Qt中使用QNetworkAccessManager类和QNetworkReply类来进行HTTP的编程。下面我们先看一个简单的例子,然后再进行扩展。
(一)最简单的实现。
1.我们新建Qt4 Gui QApplication 。
工程名为“http”,然后选中QtNetwork模块,最后Base class选择QWidget 。注意:如果新建工程时没有添加QtNetwork模块,那么就要手动在工程文件.pro中添加代码 QT += network ,表明我们使用了网络模块。
2.我们在widget.ui文件中添加一个 Text Browser ,如下图。
3..在widget.h中我们添加代码。
添加头文件:#include <QtNetwork>
私有变量private中:QNetworkAccessManager *manager;
私有槽函数private slots 中:void replyFinished(QNetworkReply *);
4.在widget.cpp文件中添加代码。
在构造函数中添加如下代码:
manager = new QNetworkAccessManager(this); //新建QNetworkAccessManager对象
connect(manager,SIGNAL(finished(QNetworkReply*)), //关联信号和槽
this,SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl(“http://www.yafeilinux.com”))); //发送请求
然后定义函数:
void Widget::replyFinished(QNetworkReply *reply) //当回复结束后
{
QTextCodec *codec = QTextCodec::codecForName(“utf8″);
//使用utf8编码,这样才可以显示中文
QString all = codec->toUnicode(reply->readAll());
ui->textBrowser->setText(all);
reply->deleteLater(); //最后要释放reply对象
}
5.运行效果如下。
6.代码分析。
上面实现了最简单的应用HTTP协议下载网页的程序。QNetworkAccessManager类用于发送网络请求和接受回复,具体的,它是用QNetworkRequest 类来管理请求,QNetworkReply类进行接收回复,并对数据进行处理。
在上面的代码中,我们使用了下面的代码来发送请求:
manager->get(QNetworkRequest(QUrl(“http://www.yafeilinux.com”)));
它返回一个QNetworkReply对象,这个下面再讲。我们只需知道只要发送请求成功,它就会下载数据。而当数据下载完成后,manager会发出finished()信号,我们对它进行了关联:
connect(manager,SIGNAL(finished(QNetworkReply*)),
this,SLOT(replyFinished(QNetworkReply*)));
也就是说,当下载数据结束时,就会执行replyFinished()函数。在这个函数中我们对接收的数据进行处理:
QTextCodec *codec = QTextCodec::codecForName(“utf8″);
QString all = codec->toUnicode(reply->readAll());
ui->textBrowser->setText(all);
这里,为了能显示下载的网页中的中文,我们使用了QTextCodec 类对象,应用utf8编码。
使用reply->readAll()函数就可以将下载的所有数据读出。然后,我们在textBrowser中将数据显示出来。当reply对象已经完成了它的功能时,我们需要将它释放,就是最后一条代码:
reply->deleteLater();
(二)功能扩展
通过上面的例子可以看到,Qt中编写基于HTTP协议的程序是十分简单的,只有十几行代码。不过,一般我们下载文件都想要看到下载进度。下面我们就更改上面的程序,让它可以下载任意的文件,并且显示下载进度。
1.我们更改widget.ui文件如下图。
这里我们添加了一个Line Edit ,一个Label ,一个Progress Bar 和一个Push Button ,它们的熟悉保持默认即可。我们在Push Button上点击鼠标右键,选择Go to slot ,然后选择clicked() ,进入其单击事件槽函数,现在我们先不写代码。
在写代码之前,我们先介绍一下整个程序执行的流程:
开始我们先让进度条隐藏。当我们在Line Edit中输入下载地址,点击下载按钮后,我们应用输入的下载地址,获得文件名,在磁盘上新建一个文件,用于保存下载的数据,然后进行链接,并显示进度 条。在下载过程中,我们将每次获得的数据都写入文件中,并更新进度条,在接收完文件后,我们重新隐藏进度条,并做一些清理工作。
根据这个思路,我们开始代码的编写。
2.我们在widget.h文件中添加代码,完成后其部分内容如下。
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
void startRequest(QUrl url); //请求链接
protected:
void changeEvent(QEvent *e);
private:
Ui::Widget *ui;
QNetworkAccessManager *manager;
QNetworkReply *reply;
QUrl url; //存储网络地址
QFile *file; //文件指针
private slots:
void on_pushButton_clicked(); //下载按钮的单击事件槽函数
void httpFinished(); //完成下载后的处理
void httpReadyRead(); //接收到数据时的处理
void updateDataReadProgress(qint64,qint64); //更新进度条
};
3.widget.cpp文件中的相关内容如下。
(1)构造函数中:
manager = new QNetworkAccessManager(this);
ui->progressBar->hide();
我们在构造函数中先隐藏进度条。等开始下载时再显示它。
(2)下载按钮的单击事件槽函数。
void Widget::on_pushButton_clicked() //下载按钮
{
url = ui->lineEdit->text();
//获取在界面中输入的url地址,如http://zz.onlinedown.net/down/laolafangkuaijin.rar
QFileInfo info(url.path());
QString fileName(info.fileName());
//获取文件名
if (fileName.isEmpty()) fileName = “index.html”;
//如果文件名为空,则使用“index.html”,
//例如使用“http://www.yafeilinux.com”时,文件名就为空
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly))
{ //如果打开文件失败,则删除file,并使file指针为0,然后返回
qDebug() << “file open error”;
delete file;
file = 0;
return;
}
startRequest(url); //进行链接请求
ui->progressBar->setValue(0); //进度条的值设为0
ui->progressBar->show(); //显示进度条
}
这里我们先从界面中获取输入的地址,然后分解出文件名。因为地址中可能没有文件名,这时我们就使用一个默认的文件名。然后我们用这个文件名新建一个文件,这个文件会保存到工程文件夹的debug文件夹下。下面我们打开文件,然后进行链接,并显示进度条。
(3)链接请求函数。
void Widget::startRequest(QUrl url) //链接请求
{
reply = manager->get(QNetworkRequest(url));
//下面关联信号和槽
connect(reply,SIGNAL(finished()),this,SLOT(httpFinished()));
//下载完成后
connect(reply,SIGNAL(readyRead()),this,SLOT(httpReadyRead()));
//有可用数据时
connect(reply,SIGNAL(downloadProgress(qint64,qint64)),
this,SLOT(updateDataReadProgress(qint64,qint64)));
//更新进度条
}
在上一个例子中我们就提到了manager->get(QNetworkRequest(url)),返回的是一个 QNetworkReply对象,这里我们获得这个对象,使用它完成显示数据下载进度的功能。这里主要是关联了几个信号和槽。当有可用数据时,reply 就会发出readyRead()信号,我们这时就可以将可用的数据保存下来。就是在这里,实现了数据分段下载保存,这样比下载完所有数据再保存,要节省很 多内存。而利用reply的downloadProgress()信号,很容易就实现了进度条的显示 。
(4)保存数据函数。
void Widget::httpReadyRead() //有可用数据
{
if (file) file->write(reply->readAll()); //如果文件存在,则写入文件
}
这里当file可用时,将下载的数据写入文件。
(5)更新进度条函数。
void Widget::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
{
ui->progressBar->setMaximum(totalBytes); //最大值
ui->progressBar->setValue(bytesRead); //当前值
}
每当有数据到来时,都更新进度条。
(6)完成下载。
void Widget::httpFinished() //完成下载
{
ui->progressBar->hide();
file->flush();
file->close();
reply->deleteLater();
reply = 0;
delete file;
file = 0;
}
这里只是当下载完成后,进行一些处理。
4.我们运行程序,效果如下。
下载网页文件:
下载华军软件园上的劳拉方块游戏:
下载完成后可以看到工程文件夹中debug文件夹中的下载的文件。
我们HTTP应用的内容就讲到这里,可以看到它是很容易的,也不需要你了解太多的 HTTP的原理知识。关于相关的类的其他使用,你可以查看其帮助。在上面的例子中,我们只是为了讲解知识,所以程序还很不完善,对于一个真正的工程,我们 还需要注意更多的细节,你可以查看Qt演示程序HTTP Client的源代码。