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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

[Qt教程]?第38篇 網(wǎng)絡(luò)(八)TCP(二)

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

版權(quán)聲明
該文章原創(chuàng)于作者yafeilinux,轉(zhuǎn)載請注明出處!

導(dǎo)語
在上一節(jié)里我們使用TCP服務(wù)器發(fā)送一個字符串,然后在TCP客戶端進行接收。在這一節(jié)將重新寫一個客戶端程序和一個服務(wù)器程序,這次實現(xiàn)客戶端進行文件的發(fā)送,服務(wù)器進行文件的接收。有了上一節(jié)的基礎(chǔ),這一節(jié)的內(nèi)容就很好理解了,注意一下幾個信號和槽的關(guān)聯(lián)即可。當(dāng)然,我們這次要更深入了解一下數(shù)據(jù)的發(fā)送和接收的處理方法。


環(huán)境:Windows Xp + Qt 4.8.5+QtCreator 2.8.0


目錄

一、客戶端 二、服務(wù)器端


正文


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

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




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

3.widget.h文件中進行更改。
1)添加頭文件包含#include?<QtNetwork>
2)添加private變量: QTcpSocket *tcpClient; ? ??QFile?*localFile;??//要發(fā)送的文件 ? ??qint64 totalBytes;??//數(shù)據(jù)總大小 ? ??qint64 bytesWritten;??//已經(jīng)發(fā)送數(shù)據(jù)大小 ? ??qint64 bytesToWrite;? ?//剩余數(shù)據(jù)大小 ? ??qint64 loadSize;? ?//每次發(fā)送數(shù)據(jù)的大小 ? ??QString?fileName;??//保存文件路徑 QByteArray outBlock;??//數(shù)據(jù)緩沖區(qū),即存放每次要發(fā)送的數(shù)據(jù)
3)添加私有槽函數(shù): private?slots: ? ??void?send();??//連接服務(wù)器 ? ??void?startTransfer();??//發(fā)送文件大小等信息 ? ??void?updateClientProgress(qint64); //發(fā)送數(shù)據(jù),更新進度條 ? ??void?displayError(QAbstractSocket::SocketError); //顯示錯誤 void openFile();??//打開文件

4.widget.cpp文件中進行更改 添加頭文件:#include?<QFileDialog>
1)在構(gòu)造函數(shù)中添加代碼: loadSize = 4*1024; totalBytes = 0; bytesWritten = 0; bytesToWrite = 0; tcpClient = new QTcpSocket(this); //當(dāng)連接服務(wù)器成功時,發(fā)出connected()信號,我們開始傳送文件 connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer())); //當(dāng)有數(shù)據(jù)發(fā)送成功時,我們更新進度條 connect(tcpClient,SIGNAL(bytesWritten(qint64)),this, ? ?? ??SLOT(updateClientProgress(qint64))); connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this, ? ?? ??SLOT(displayError(QAbstractSocket::SocketError))); //開始使”發(fā)送“按鈕不可用 ui->sendButton->setEnabled(false);
我們主要是進行了變量的初始化和幾個信號和槽函數(shù)的關(guān)聯(lián)。

2)實現(xiàn)打開文件函數(shù)。 void?Widget::openFile()? ?//打開文件 { ? ??fileName = QFileDialog::getOpenFileName(this); ? ??if(!fileName.isEmpty()) ? ??{ ? ?? ??ui->sendButton->setEnabled(true); ? ?? ??ui->clientStatusLabel->setText(tr("打開文件?%1?成功!") ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?.arg(fileName)); ? ??} } 該函數(shù)將在下面的“打開”按鈕單擊事件槽函數(shù)中調(diào)用。
(3)實現(xiàn)連接函數(shù)。 void?Widget::send()? ?//連接到服務(wù)器,執(zhí)行發(fā)送 { ? ??ui->sendButton->setEnabled(false); ? ??bytesWritten = 0; ? ??//初始化已發(fā)送字節(jié)為0 ? ??ui->clientStatusLabel->setText(tr("連接中...")); ? ??tcpClient->connectToHost(ui->hostLineEdit->text(), ? ?? ?? ?? ?? ?? ?? ?? ?? ???ui->portLineEdit->text().toInt());//連接 } 該函數(shù)將在“發(fā)送”按鈕的單擊事件槽函數(shù)中調(diào)用。

(4)實現(xiàn)文件頭結(jié)構(gòu)的發(fā)送。 void?Widget::startTransfer()??//實現(xiàn)文件大小等信息的發(fā)送 { ? ??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)); ? ? ? ??//發(fā)送完頭數(shù)據(jù)后剩余數(shù)據(jù)的大小 ? ??bytesToWrite?=?totalBytes?-?tcpClient->write(outBlock); ? ? ? ??ui->clientStatusLabel->setText(tr("已連接")); ? ??outBlock.resize(0); }

5)下面是更新進度條,也就是發(fā)送文件數(shù)據(jù)。
//更新進度條,實現(xiàn)文件的傳送 void?Widget::updateClientProgress(qint64?numBytes) { ? ??//已經(jīng)發(fā)送數(shù)據(jù)的大小 ? ??bytesWritten?+=?(int)numBytes; ? ? ? ??if(bytesToWrite?>?0)?//如果已經(jīng)發(fā)送了數(shù)據(jù) ? ??{ ? ?//每次發(fā)送loadSize大小的數(shù)據(jù),這里設(shè)置為4KB,如果剩余的數(shù)據(jù)不足4KB, ? ?//就發(fā)送剩余數(shù)據(jù)的大小 ? ?? ??outBlock?=?localFile->read(qMin(bytesToWrite,loadSize)); ? ?? ? ? ?? ??//發(fā)送完一次數(shù)據(jù)后還剩余數(shù)據(jù)的大小 ? ?? ??bytesToWrite?-=?(int)tcpClient->write(outBlock); ? ?? ? ? ?? ??//清空發(fā)送緩沖區(qū) ? ?? ??outBlock.resize(0); ? ?? ? ? ??}?else?{ ? ?? ??localFile->close();?//如果沒有發(fā)送任何數(shù)據(jù),則關(guān)閉文件 ? ??} ? ? ? ??//更新進度條 ? ??ui->clientProgressBar->setMaximum(totalBytes); ? ??ui->clientProgressBar->setValue(bytesWritten); ? ? ? ??if(bytesWritten?==?totalBytes)?//發(fā)送完畢 ? ??{ ? ???ui->clientStatusLabel->setText(tr("傳送文件?%1?成功") .arg(fileName)); ? ?? ??localFile->close(); ? ?? ??tcpClient->close(); ? ??} }

(6)實現(xiàn)錯誤處理函數(shù)。 void?Widget::displayError(QAbstractSocket::SocketError) //顯示錯誤 { ? ??qDebug() << tcpClient->errorString(); ? ??tcpClient->close(); ? ??ui->clientProgressBar->reset(); ? ??ui->clientStatusLabel->setText(tr("客戶端就緒")); ? ??ui->sendButton->setEnabled(true); }
(7)我們從widget.ui中分別進行“打開”按鈕和“發(fā)送”按鈕的單擊事件槽函數(shù),然后更改如下。 void?Widget::on_openButton_clicked() //打開按鈕 { ? ??openFile(); } void?Widget::on_sendButton_clicked() //發(fā)送按鈕 { ? ??send(); }
5.我們?yōu)榱耸钩绦蛑械闹形牟伙@示亂碼,在main.cpp文件中更改。 添加頭文件:#include?<QTextCodec> main函數(shù)中添加代碼:QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
6.現(xiàn)在可以先運行程序。
7.程序整體思路分析。 我們設(shè)計好界面,然后按下“打開”按鈕,選擇要發(fā)送的文件,這時調(diào)用了openFile()函數(shù)。然后點擊“發(fā)送”按鈕,調(diào)用send()函數(shù),與服務(wù)器進行連接。當(dāng)連接成功時就會發(fā)出connected()信號,這時就會執(zhí)行startTransfer()函數(shù),進行文件頭結(jié)構(gòu)的發(fā)送,當(dāng)發(fā)送成功時就會發(fā)出bytesWritten(qint64)信號,這時執(zhí)行updateClientProgress(qint64 numBytes)進行文件數(shù)據(jù)的傳輸和進度條的更新。這里使用了一個loadSize變量,我們在構(gòu)造函數(shù)中將其初始化為4*10244字節(jié),它的作用是,我們將整個大的文件分成很多小的部分進行發(fā)送,每部分為4字節(jié)。而當(dāng)連接出現(xiàn)問題時就會發(fā)出error(QAbstractSocket::SocketError)信號,這時就會執(zhí)行displayError()函數(shù)。對于程序中其他細(xì)節(jié)我們就不再分析,希望大家能自己編程研究一下。
二、服務(wù)器端 我們在服務(wù)器端進行數(shù)據(jù)的接收。服務(wù)器端程序是很簡單的,我們開始進行監(jiān)聽,一旦發(fā)現(xiàn)有連接請求就發(fā)出newConnection()信號,然后我們便接受連接,開始接收數(shù)據(jù)。
1.新建QtGui應(yīng)用 名稱為tcpReceiver,基類選擇QWidget,類名為Widget,完成后打開tcpReceiver.pro添加一行代碼:QT += network 。
2.我們更改widget.ui文件,設(shè)計界面如下。 其中“服務(wù)器端”LabelobjectNameserverStatusLabel;進度條ProgressBarobjectNameserverProgressBar,設(shè)置其value屬性為0;“開始監(jiān)聽”按鈕的objectNamestartButton 效果如下。




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

2)添加私有變量: ? ???QTcpServer?tcpServer; ? ??QTcpSocket?*tcpServerConnection; ? ??qint64 totalBytes;??//存放總大小信息 ? ??qint64 bytesReceived;??//已收到數(shù)據(jù)的大小 ? ??qint64 fileNameSize;??//文件名的大小信息 ? ??QString?fileName;? ?//存放文件名 ? ??QFile?*localFile;? ?//本地文件 QByteArray inBlock;? ?//數(shù)據(jù)緩沖區(qū)
3)添加私有槽函數(shù): private?slots: ? ??void?on_startButton_clicked(); ? ??void?start();? ?//開始監(jiān)聽 ? ??void?acceptConnection();??//建立連接 void updateServerProgress();??//更新進度條,接收數(shù)據(jù)
//顯示錯誤 void displayError(QAbstractSocket::SocketError socketError);


4.更改widget.cpp文件。

1)在構(gòu)造函數(shù)中添加代碼: totalBytes = 0; ? ??bytesReceived = 0; fileNameSize = 0;
//當(dāng)發(fā)現(xiàn)新連接時發(fā)出newConnection()信號 ? ??connect(&tcpServer,SIGNAL(newConnection()),this, SLOT(acceptConnection()));

2)實現(xiàn)start()函數(shù)。 void?Widget::start() //開始監(jiān)聽 { ? ??ui->startButton->setEnabled(false); ? ??bytesReceived =0; ? ??if(!tcpServer.listen(QHostAddress::LocalHost,6666)) ? ??{ ? ?? ??qDebug() << tcpServer.errorString(); ? ?? ??close(); ? ?? ??return; ? ??} ? ??ui->serverStatusLabel->setText(tr("監(jiān)聽")); }
(3)實現(xiàn)接受連接函數(shù)。 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)實現(xiàn)更新進度條函數(shù)。 void?Widget::updateServerProgress()??//更新進度條,接收數(shù)據(jù) { ? ?QDataStream?in(tcpServerConnection); ? ?in.setVersion(QDataStream::Qt_4_6); ? ?if(bytesReceived <= sizeof(qint64)*2) { //如果接收到的數(shù)據(jù)小于16個字節(jié),那么是剛開始接收數(shù)據(jù),我們保存到//來的頭文件信息 ? ?? ??if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2) ? ?? ?? ???&&?(fileNameSize == 0)) ? ?? ??{?//接收數(shù)據(jù)總大小信息和文件名大小信息 ? ?? ?? ???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) ? ?{??//如果接收的數(shù)據(jù)小于總數(shù)據(jù),那么寫入文件 ? ?? ?bytesReceived += tcpServerConnection->bytesAvailable(); ? ?? ?inBlock= tcpServerConnection->readAll(); ? ?? ?localFile->write(inBlock); ? ?? ?inBlock.resize(0); ? ?} //更新進度條 ? ?ui->serverProgressBar->setMaximum(totalBytes); ? ?ui->serverProgressBar->setValue(bytesReceived); ? ? ? ?if(bytesReceived == totalBytes) ? ?{?//接收數(shù)據(jù)完成時 ? ??tcpServerConnection->close(); ? ??localFile->close(); ? ??ui->startButton->setEnabled(true); ui->serverStatusLabel->setText(tr("接收文件 %1 成功!") .arg(fileName)); ? ?} }

5)錯誤處理函數(shù)。 void?Widget::displayError(QAbstractSocket::SocketError) //錯誤處理 { ? ??qDebug() << tcpServerConnection->errorString(); ? ??tcpServerConnection->close(); ? ??ui->serverProgressBar->reset(); ? ??ui->serverStatusLabel->setText(tr("服務(wù)端就緒")); ? ??ui->startButton->setEnabled(true); }
(6)我們在widget.ui中進入“開始監(jiān)聽”按鈕的單擊事件槽函數(shù),更改如下。 void?Widget::on_startButton_clicked() //開始監(jiān)聽按鈕 { ? ??start(); }
5.我們?yōu)榱耸钩绦蛑械闹形牟伙@示亂碼,在main.cpp文件中更改。 添加頭文件包含:#include?<QTextCodec> main函數(shù)中添加代碼:QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
6.運行程序,并同時運行tcpSender程序,效果如下。




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

結(jié)語


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



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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。