解析 Qt網絡之TCP 下篇 網絡學習基礎
解析 Qt網絡之TCP 下篇 網絡學習基礎是本文要介紹的內容,接著上一篇繼續介紹。解析Qt網絡之TCP 上篇 網絡學習基礎 在上一節里我們使用TCP服務器發送一個字符串,然后在TCP客戶端進行接收。在這一節我們重新寫一個客戶端程序和一個服務器程序,這次我們讓客戶端進行文件的發送,服務器進行文件的接收。有了上一節的基礎,這一節的內容就很好理解了,注意一下幾個信號和槽的關聯即可。當然,我們這次要更深入了解一下數據的發送和接收的處理方法。
一、客戶端
這次我們先講解客戶端,在客戶端里我們與服務器進行連接,一旦連接成功,就會發出connected()信號,這時我們就進行文件的發送。
在上一節我們已經看到,發送數據時我們先發送了數據的大小信息。這一次,我們要先發送文件的總大小,然后文件名長度,然后是文件名,這三部分我們合稱為文件頭結構,最后再發送文件數據。所以在發送函數里我們就要進行相應的處理,當然,在服務器的接收函數里我們也要進行相應的處理。對于文件大小,這次我們使用了qint64,它是64位的,可以表示一個很大的文件了。
1.同前一節,我們新建工程,將工程命名為“tcpSender”。注意添加network模塊。
2.我們在widget.ui文件中將界面設計如下。
這里“主機”后的Line Edit的objectName為hostLineEdit;“端口”后的Line Edit的objectName為portLineEdit;下面的Progress Bar的objectName為clientProgressBar,其value屬性設為0;“狀態”Label的objetName為clientStatusLabel;“打開”按鈕的objectName為openButton;“發送”按鈕的objectName為sendButton;
3.在widget.h 文件中進行更改。
(1)添加頭文件#include <QtNetwork>
(2)添加private變量:
- QTcpSocket *tcpClient;
- QFile *localFile; //要發送的文件
- qint64 totalBytes; //數據總大小
- qint64 bytesWritten; //已經發送數據大小
- qint64 bytesToWrite; //剩余數據大小
- qint64 loadSize; //每次發送數據的大小
- QString fileName; //保存文件路徑
- QByteArray outBlock; //數據緩沖區,即存放每次要發送的數據
(3)添加私有槽函數:
- private slots:
- void send(); //連接服務器
- void startTransfer(); //發送文件大小等信息
- void updateClientProgress(qint64); //發送數據,更新進度條
- void displayError(QAbstractSocket::SocketError); //顯示錯誤
- void openFile(); //打開文件
4.在widget.cpp文件中進行更改。
添加頭文件:#include <QFileDialog>
(1)在構造函數中添加代碼:
- loadSize = 4*1024;
- totalBytes = 0;
- bytesWritten = 0;
- bytesToWrite = 0;
- tcpClient = new QTcpSocket(this);
- connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));
- //當連接服務器成功時,發出connected()信號,我們開始傳送文件
- connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,
- SLOT(updateClientProgress(qint64)));
- //當有數據發送成功時,我們更新進度條
- connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,
- SLOT(displayError(QAbstractSocket::SocketError)));
- ui->sendButton->setEnabled(false);
- //開始使”發送“按鈕不可用
我們主要是進行了變量的初始化和幾個信號和槽函數的關聯。
(2)實現打開文件函數。
- void Widget::openFile() //打開文件
- {
- fileName = QFileDialog::getOpenFileName(this);
- if(!fileName.isEmpty())
- {
- ui->sendButton->setEnabled(true);
- ui->clientStatusLabel->setText(tr(“打開文件 %1 成功!”)
- .arg(fileName));
- }
- }
該函數將在下面的“打開”按鈕單擊事件槽函數中調用。
(3)實現連接函數。
- void Widget::send() //連接到服務器,執行發送
- {
- ui->sendButton->setEnabled(false);
- bytesWritten = 0;
- //初始化已發送字節為0
- ui->clientStatusLabel->setText(tr(“連接中…”));
- tcpClient->connectToHost(ui->hostLineEdit->text(),
- ui->portLineEdit->text().toInt());//連接
- }
該函數將在“發送”按鈕的單擊事件槽函數中調用。
(4)實現文件頭結構的發送。
- void Widget::startTransfer() //實現文件大小等信息的發送
- {
- localFile = new QFile(fileName);
- if(!localFile->open(QFile::ReadOnly))
- {
- qDebug() << "open file error!";
- return;
- }
- totalBytes = localFile->size();
- //文件總大小
- QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
- sendOut.setVersion(QDataStream::Qt_4_6);
- QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);
- sendOut << qint64(0) << qint64(0) << currentFileName;
- //依次寫入總大小信息空間,文件名大小信息空間,文件名
- totalBytes += outBlock.size();
- //這里的總大小是文件名大小等信息和實際文件大小的總和
- sendOut.device()->seek(0);
- sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2));
- //返回outBolock的開始,用實際的大小信息代替兩個qint64(0)空間
- bytesToWrite = totalBytes - tcpClient->write(outBlock);
- //發送完頭數據后剩余數據的大小
- ui->clientStatusLabel->setText(tr("已連接"));
- outBlock.resize(0);
- }
(5)下面是更新進度條,也就是發送文件數據。
- void Widget::updateClientProgress(qint64 numBytes) //更新進度條,實現文件的傳送
- {
- bytesWritten += (int)numBytes;
- //已經發送數據的大小
- if(bytesToWrite > 0) //如果已經發送了數據
- {
- outBlock = localFile->read(qMin(bytesToWrite,loadSize));
- //每次發送loadSize大小的數據,這里設置為4KB,如果剩余的數據不足4KB,
- //就發送剩余數據的大小
- bytesToWrite -= (int)tcpClient->write(outBlock);
- //發送完一次數據后還剩余數據的大小
- outBlock.resize(0);
- //清空發送緩沖區
- }
- else
- {
- localFile->close(); //如果沒有發送任何數據,則關閉文件
- }
- ui->clientProgressBar->setMaximum(totalBytes);
- ui->clientProgressBar->setValue(bytesWritten);
- //更新進度條
- if(bytesWritten == totalBytes) //發送完畢
- {
- ui->clientStatusLabel->setText(tr(“傳送文件 %1 成功”).arg(fileName));
- localFile->close();
- tcpClient->close();
- }
- }
(6)實現錯誤處理函數。
- void Widget::displayError(QAbstractSocket::SocketError) //顯示錯誤
- {
- qDebug() << tcpClient->errorString();
- tcpClient->close();
- ui->clientProgressBar->reset();
- ui->clientStatusLabel->setText(tr(“客戶端就緒”));
- ui->sendButton->setEnabled(true);
- }
(7)我們從widget.ui中分別進行“打開”按鈕和“發送”按鈕的單擊事件槽函數,然后更改如下。
- void Widget::on_openButton_clicked() //打開按鈕
- {
- openFile();
- }
- void Widget::on_sendButton_clicked() //發送按鈕
- {
- send();
- }
5.我們為了使程序中的中文不顯示亂碼,在main.cpp文件中更改。
添加頭文件:#include <QTextCodec>
在main函數中添加代碼:QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
6.運行程序,效果如下。
7.程序整體思路分析。
我們設計好界面,然后按下“打開”按鈕,選擇我們要發送的文件,這時調用了openFile()函數。然后我們點擊“發送”按鈕,調用send()函數,與服務器進行連接。當連接成功時就會發出connected()信號,這時就會執行startTransfer()函數,進行文件頭結構的發送,當發送成功時就會發出bytesWritten(qint64)信號,這時我們執行updateClientProgress(qint64 numBytes)進行文件數據的傳輸和進度條的更新。這里使用了一個loadSize變量,我們在構造函數中將其初始化為4*1024即4字節,它的作用是,我們將整個大的文件分成很多小的部分進行發送,每部分為4字節。而當連接出現問題時就會發出error(QAbstractSocket::SocketError)信號,這時就會執行displayError()函數。對于程序中其他細節我們就不再分析,希望大家能自己編程研究一下。
#p#
二、服務器端。
我們在服務器端進行數據的接收。服務器端程序是很簡單的,我們開始進行監聽,一旦發現有連接請求就發出newConnection()信號,然后我們便接受連接,開始接收數據。
1、新建工程,名字為“tcpReceiver”。
2、我們更改widget.ui文件,設計界面如下。
其中“服務器端”Label的objectName為serverStatusLabel;進度條Progress Bar的objectName為serverProgressBar,設置其value屬性為0;“開始監聽”按鈕的objectName為startButton。
效果如下。
3、更改widget.h文件的內容。
(1)添加頭文件:#include <QtNetwork>
(2)添加私有變量:
- QTcpServer tcpServer;
- QTcpSocket *tcpServerConnection;
- qint64 totalBytes; //存放總大小信息
- qint64 bytesReceived; //已收到數據的大小
- qint64 fileNameSize; //文件名的大小信息
- QString fileName; //存放文件名
- QFile *localFile; //本地文件
- eArray inBlock; //數據緩沖區
(3)添加私有槽函數:
- private slots:
- void on_startButton_clicked();
- void start(); //開始監聽
- void acceptConnection(); //建立連接
- void updateServerProgress(); //更新進度條,接收數據
- void displayError(QAbstractSocket::SocketError socketError);
- //顯示錯誤
4、更改widget.cpp文件。
(1)在構造函數中添加代碼:
- totalBytes = 0;
- bytesReceived = 0;
- fileNameSize = 0;
- connect(&tcpServer,SIGNAL(newConnection()),this,
- SLOT(acceptConnection()));
//當發現新連接時發出newConnection()信號
(2)實現start()函數。
- void Widget::start() //開始監聽
- {
- ui->startButton->setEnabled(false);
- bytesReceived =0;
- if(!tcpServer.listen(QHostAddress::LocalHost,6666))
- {
- qDebug() << tcpServer.errorString();
- close();
- return;
- }
- ui->serverStatusLabel->setText(tr(“監聽”));
- }
(3)實現接受連接函數。
- void Widget::acceptConnection() //接受連接
- {
- tcpServertcpServerConnection = tcpServer.nextPendingConnection();
- connect(tcpServerConnection,SIGNAL(readyRead()),this,
- SLOT(updateServerProgress()));
- connect(tcpServerConnection,
- SIGNAL(error(QAbstractSocket::SocketError)),this,
- SLOT(displayError(QAbstractSocket::SocketError)));
- ui->serverStatusLabel->setText(tr(“接受連接”));
- tcpServer.close();
- }
(4)實現更新進度條函數。
- void Widget::updateServerProgress() //更新進度條,接收數據
- {
- QDataStream in(tcpServerConnection);
- in.setVersion(QDataStream::Qt_4_6);
- if(bytesReceived <= sizeof(qint64)*2)
- { //如果接收到的數據小于16個字節,那么是剛開始接收數據,我們保存到//來的頭文件信息
- if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2)
- && (fileNameSize == 0))
- { //接收數據總大小信息和文件名大小信息
- in >> totalBytes >> fileNameSize;
- bytesReceived += sizeof(qint64) * 2;
- }
- if((tcpServerConnection->bytesAvailable() >= fileNameSize)
- && (fileNameSize != 0))
- { //接收文件名,并建立文件
- in >> fileName;
- ui->serverStatusLabel->setText(tr(“接收文件 %1 …”)
- .arg(fileName));
- bytesReceived += fileNameSize;
- localFile = new QFile(fileName);
- if(!localFile->open(QFile::WriteOnly))
- {
- qDebug() << “open file error!”;
- return;
- }
- }
- else return;
- }
- if(bytesReceived < totalBytes)
- { //如果接收的數據小于總數據,那么寫入文件
- bytesReceived += tcpServerConnection->bytesAvailable();
- inBlock = tcpServerConnection->readAll();
- localFile->write(inBlock);
- inBlock.resize(0);
- }
- ui->serverProgressBar->setMaximum(totalBytes);
- ui->serverProgressBar->setValue(bytesReceived);
- //更新進度條
- if(bytesReceived == totalBytes)
- { //接收數據完成時
- tcpServerConnection->close();
- localFile->close();
- ui->startButton->setEnabled(true);
- ui->serverStatusLabel->setText(tr(“接收文件 %1 成功!”)
- .arg(fileName));
- }
- }
(5)錯誤處理函數。
- void Widget::displayError(QAbstractSocket::SocketError) //錯誤處理
- {
- qDebug() << tcpServerConnection->errorString();
- tcpServerConnection->close();
- ui->serverProgressBar->reset();
- ui->serverStatusLabel->setText(tr(“服務端就緒”));
- ui->startButton->setEnabled(true);
- }
(6)我們在widget.ui中進入“開始監聽”按鈕的單擊事件槽函數,更改如下。
- void Widget::on_startButton_clicked() //開始監聽按鈕
- {
- start();
- }
5.我們為了使程序中的中文不顯示亂碼,在main.cpp文件中更改。
添加頭文件:#include <QTextCodec>
在main函數中添加代碼:QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
6.運行程序,并同時運行tcpSender程序,效果如下。
我們先在服務器端按下“開始監聽”按鈕,然后在客戶端輸入主機地址和端口號,然后打開要發送的文件,點擊“發送”按鈕進行發送。
在這兩節里我們介紹了TCP的應用,可以看到服務器端和客戶度端都可以當做發送端或者接收端,而且數據的發送與接收只要使用相對應的協議即可,它是可以根據用戶的需要來進行編程的,沒有固定的格式。
本文章原創于 www.yafeilinux.com
小結:Qt網絡之TCP 下篇 網絡學習基礎的內容介紹完了,希望本文內容對你有所幫助!最后推薦相關的資料:】
http://mobile.51cto.com/symbian-268176.htm qt網絡
http://mobile.51cto.com/symbian-268170.htm http 編程
http://mobile.51cto.com/symbian-268167_1.htm 獲取網絡地址