看了兩天的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的源代碼。