日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[Qt教程] 第38篇 网络(八)TCP(二)

發布時間:2024/4/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Qt教程] 第38篇 网络(八)TCP(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

[Qt教程]?第38篇 網絡(八)TCP(二)

樓主 ?發表于 2013-9-6 15:50:35?|?查看: 421|?回復: 3
TCP(二)

版權聲明
該文章原創于作者yafeilinux,轉載請注明出處!

導語
在上一節里我們使用TCP服務器發送一個字符串,然后在TCP客戶端進行接收。在這一節將重新寫一個客戶端程序和一個服務器程序,這次實現客戶端進行文件的發送,服務器進行文件的接收。有了上一節的基礎,這一節的內容就很好理解了,注意一下幾個信號和槽的關聯即可。當然,我們這次要更深入了解一下數據的發送和接收的處理方法。


環境:Windows Xp + Qt 4.8.5+QtCreator 2.8.0


目錄

一、客戶端 二、服務器端


正文


一、客戶端 這次先講解客戶端,在客戶端里需要與服務器進行連接,一旦連接成功,就會發出connected()信號,這時我們就進行文件的發送。 在上一節已經看到,發送數據時先發送了數據的大小信息。這一次,我們要先發送文件的總大小,然后文件名長度,然后是文件名,這三部分合稱為文件頭結構,最后再發送文件數據。所以在發送函數里就要進行相應的處理,當然,在服務器的接收函數里也要進行相應的處理。對于文件大小,這次使用了qint64,它是64位的,可以表示一個很大的文件了。

1.新建QtGui項目 名稱為tcpSender,基類選擇QWidget,類名為Widget,完成后打開tcpSender.pro添加一行代碼:QT += network 。
2.我們在widget.ui文件中將界面設計如下。




這里“主機”后的Line EditobjectNamehostLineEdit;“端口”后的Line EditobjectNameportLineEdit;下面的Progress BarobjectNameclientProgressBar,其value屬性設為0;“狀態”LabelobjetNameclientStatusLabel;“打開”按鈕的objectNameopenButton;“發送”按鈕的objectNamesendButton

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); //當連接服務器成功時,發出connected()信號,我們開始傳送文件 connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer())); //當有數據發送成功時,我們更新進度條 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); ? ??//返回outBolock的開始,用實際的大小信息代替兩個qint64(0)空間 ? ??sendOut<<totalBytes<<qint64((outBlock.size()?-?sizeof(qint64)*2)); ? ? ? ??//發送完頭數據后剩余數據的大小 ? ??bytesToWrite?=?totalBytes?-?tcpClient->write(outBlock); ? ? ? ??ui->clientStatusLabel->setText(tr("已連接")); ? ??outBlock.resize(0); }

5)下面是更新進度條,也就是發送文件數據。
//更新進度條,實現文件的傳送 void?Widget::updateClientProgress(qint64?numBytes) { ? ??//已經發送數據的大小 ? ??bytesWritten?+=?(int)numBytes; ? ? ? ??if(bytesToWrite?>?0)?//如果已經發送了數據 ? ??{ ? ?//每次發送loadSize大小的數據,這里設置為4KB,如果剩余的數據不足4KB, ? ?//就發送剩余數據的大小 ? ?? ??outBlock?=?localFile->read(qMin(bytesToWrite,loadSize)); ? ?? ? ? ?? ??//發送完一次數據后還剩余數據的大小 ? ?? ??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::codecForName("UTF-8"));
6.現在可以先運行程序。
7.程序整體思路分析。 我們設計好界面,然后按下“打開”按鈕,選擇要發送的文件,這時調用了openFile()函數。然后點擊“發送”按鈕,調用send()函數,與服務器進行連接。當連接成功時就會發出connected()信號,這時就會執行startTransfer()函數,進行文件頭結構的發送,當發送成功時就會發出bytesWritten(qint64)信號,這時執行updateClientProgress(qint64 numBytes)進行文件數據的傳輸和進度條的更新。這里使用了一個loadSize變量,我們在構造函數中將其初始化為4*10244字節,它的作用是,我們將整個大的文件分成很多小的部分進行發送,每部分為4字節。而當連接出現問題時就會發出error(QAbstractSocket::SocketError)信號,這時就會執行displayError()函數。對于程序中其他細節我們就不再分析,希望大家能自己編程研究一下。
二、服務器端 我們在服務器端進行數據的接收。服務器端程序是很簡單的,我們開始進行監聽,一旦發現有連接請求就發出newConnection()信號,然后我們便接受連接,開始接收數據。
1.新建QtGui應用 名稱為tcpReceiver,基類選擇QWidget,類名為Widget,完成后打開tcpReceiver.pro添加一行代碼:QT += network 。
2.我們更改widget.ui文件,設計界面如下。 其中“服務器端”LabelobjectNameserverStatusLabel;進度條ProgressBarobjectNameserverProgressBar,設置其value屬性為0;“開始監聽”按鈕的objectNamestartButton 效果如下。




3.更改widget.h文件的內容。
1)添加頭文件包含:#include?<QtNetwork>

2)添加私有變量: ? ???QTcpServer?tcpServer; ? ??QTcpSocket?*tcpServerConnection; ? ??qint64 totalBytes;??//存放總大小信息 ? ??qint64 bytesReceived;??//已收到數據的大小 ? ??qint64 fileNameSize;??//文件名的大小信息 ? ??QString?fileName;? ?//存放文件名 ? ??QFile?*localFile;? ?//本地文件 QByteArray 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;
//當發現新連接時發出newConnection()信號 ? ??connect(&tcpServer,SIGNAL(newConnection()),this, SLOT(acceptConnection()));

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()??//接受連接 { ? ??tcpServerConnection = 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::codecForName("UTF-8"));
6.運行程序,并同時運行tcpSender程序,效果如下。




我們先在服務器端按下“開始監聽”按鈕,然后在客戶端輸入主機地址和端口號,然后打開要發送的文件,點擊“發送”按鈕進行發送。

結語


在這兩節里我們介紹了TCP的應用,可以看到服務器端和客戶度端都可以當做發送端或者接收端,而且數據的發送與接收只要使用相對應的協議即可,它是可以根據用戶的需要來進行編程的,沒有固定的格式。《Qt及Qt Quick開發實戰精解》中的局域網聊天工具就是本節知識的擴展,大家可以從社區下載頁面下載其源碼。



涉及到的源碼:??tcpSender.rar?(3.55 KB, 下載次數: 12)??tcpReceiver.rar?(3.02 KB, 下載次數: 12)?

總結

以上是生活随笔為你收集整理的[Qt教程] 第38篇 网络(八)TCP(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。