qt下的udp、tcp網(wǎng)絡(luò)編程_第1頁
qt下的udp、tcp網(wǎng)絡(luò)編程_第2頁
免費預(yù)覽已結(jié)束,剩余42頁可下載查看

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、QT 網(wǎng)絡(luò)編程一 TCP TCP 即 Transmission Control Protocol ,傳輸控制協(xié)議。與 UDP 不同,它是面向連接和數(shù)據(jù) 流的可靠傳輸協(xié)議。也就是說,它能使一臺計算機上的數(shù)據(jù)無差錯的發(fā)往網(wǎng)絡(luò)上的其他計算機, 所以當(dāng)要傳輸大量數(shù)據(jù)時,我們選用 TCP 協(xié)議。 TCP 協(xié)議的程序使用的是客戶端 /服務(wù)器模式,在 Qt 中提供了 QTcpSocket 類來編寫客戶端程 序,使用 QTcpServer 類編寫服務(wù)器端程序。我們在服務(wù)器端進行端口的監(jiān)聽,一旦發(fā)現(xiàn)客戶 端的連接請求,就會發(fā)岀 newConnection() 信號,我們可以關(guān)聯(lián)這個信號到我們自己的槽函數(shù), 進行

2、數(shù)據(jù)的發(fā)送。而在客戶端,一旦有數(shù)據(jù)到來就會發(fā)岀 readyRead() 信號,我們可以關(guān)聯(lián)此 信號,進行數(shù)據(jù)的接收。其實,在程序中最難理解的地方就是程序的發(fā)送和接收了, 為了讓大家 更好的理解,我們在這一節(jié)只是講述一個傳輸簡單的字符串的例子, 在下一節(jié)再進行擴展, 實現(xiàn) 任意文件的傳輸。 一、服務(wù)器端。 在服務(wù)器端的程序中,我們監(jiān)聽本地主機的一個端口,這里使用 6666,然后我們關(guān)聯(lián) newConnection() 信號與自己寫的 sendMessage() 槽函數(shù)。就是說一旦有客戶端的連接請求, 就會執(zhí)行 sendMessage() 函數(shù),在這個函數(shù)里我們發(fā)送一個簡單的字符串。 1. 我們新

3、建 Qt4 Gui Application ,工程名為 “ tcpServer ”選中 QtNetwork 模塊,Base class 選擇 QWidget 。(說明:如果一些 Qt Creator 版本沒有添加模塊一項,我們就需要在 工程文件 tcpS 中添加一行代碼: QT += network ) 2. 我們在 widget.ui 的設(shè)計區(qū)添加一個 Label ,更改其 objectName 為 statusLabel , 用于顯示一些狀態(tài)信息。如下:疇待連接 3. 在 widget.h 文件中做以下更改。 添加頭文件:#i nclude 添加 private 對象:

4、QTcpServer *tcpServer; 添加私有槽函數(shù): private slots: void sen dMessage(); 4. 在 widget.cpp 文件中進行更改。 在其構(gòu)造函數(shù)中添加代碼: tcpServer = new QTcpServer(this); if(!tcpServer-liste n(QHostAddress:LocalHost,6666) /監(jiān)聽本地主機的 6666 端口,如果岀錯就輸岀錯誤信息,并關(guān)閉 qDebug() errorStri ng(); close(); conn ect(tcpServer,SIGNAL( newCo nn ectio

5、n(),this,SLOT(se ndMessage(); /連接信號和相應(yīng)槽函數(shù) 我們在構(gòu)造函數(shù)中使用 tcpServer 的 listen()函數(shù)進行監(jiān)聽,然后關(guān)聯(lián)了 newConnection() 和我們自己的 sen dMessage() 函數(shù)。 下面我們實現(xiàn) sen dMessage() 函數(shù)。 void Widget:se ndMessage() QByteArray block; / 用于暫存我們要發(fā)送的數(shù)據(jù) QDataStream out(&block,QIODevice:Write On ly); /使用數(shù)據(jù)流寫入數(shù)據(jù) out.setVersio n(QDataStr

6、eam:Qt_4_6); /設(shè)置數(shù)據(jù)流的版本,客戶端和服務(wù)器端使用的版本要相同 out(qui nt16) 0; outseek(0); outn extPe ndingConn ectio n(); /我們獲取已經(jīng)建立的連接的子套接字 conn ect(clie ntConn ecti on ,SIGNAL(disc onn ected(),clie ntConn ecti on, SLOT(deleteLater(); clie ntConn ectio n-write(block); clie ntConn ecti on-disc onn ectFromHost(); ui-statu

7、sLabel- setText( “ send message successful! ” ); /發(fā)送數(shù)據(jù)成功后,顯示提示 這個是數(shù)據(jù)發(fā)送函數(shù),我們主要介紹兩點: (1)為了保證在客戶端能接收到完整的文件,我們都在數(shù)據(jù)流的最開始寫入完整文件的大小信 息,這樣客戶端就可以根據(jù)大小信息來判斷是否接受到了完整的文件。 而在服務(wù)器端,我們在發(fā) 送數(shù)據(jù)時就要首先發(fā)送實際文件的大小信息, 但是,文件的大小一開始是無法預(yù)知的, 所以我們 先使用了 out(quint16) 0; 在 block 的開始添加了一個 quint16 大小的空間,也就是兩字 節(jié)的空間,它用于后面放置文件的大小信息。然后 outs

8、eek(0); 返回到 block 的開始, 加入實際的文件大小信息,也就是后面的代碼,它是實際文件的大小: out(qui nt16) (block.size() - sizeof(qu in t16); (2 )在服務(wù)器端我們可以使用 tcpServer 的 nextPendingConnection() 函數(shù)來獲取已經(jīng)建立 的連接的 Tcp 套接字,使用它來完成數(shù)據(jù)的發(fā)送和其它操作。比如這里,我們關(guān)聯(lián)了 disconnected() 信號和 deleteLater() 槽函數(shù),然后我們發(fā)送數(shù)據(jù) clie ntConn ectio n-write(block); 然后是 clientCon

9、nection-disconnectFromHost(); 它表示當(dāng)發(fā)送完成時就會斷開連接,這 時就會發(fā)岀 disc onn ected() 信號,而最后調(diào)用 deleteLater() 函數(shù)保證在關(guān)閉連接后刪除該套 接字 clientConnection 。 5. 這樣服務(wù)器的程序就完成了,我們先運行一下程序。 、客戶端。 我們在客戶端程序中向服務(wù)器發(fā)送連接請求,當(dāng)連接成功時接收服務(wù)器發(fā)送的數(shù)據(jù)。 1.我們新建 Qt4 Gui Application ,工程名為 “ tcpCIient ,選中 QtNetwork 模塊,Base class 選擇 QWidget 。 2,我們在 widget

10、.ui 中添加幾個標(biāo)簽 Label和兩個 Line Edit 以及一個按鈕 Push Butt on 收至1的信息. 其中 主機后的 Line Edit 的 objectName 為 hostLineEdit , 端口號后的為 portLineEdit 收到的信息 標(biāo)簽的 objectName 為 messageLabel messageLabel 。 3. 在 widget.h 文件中做更改。 添加頭文件:#i nclude 添加 private 變量: QTcpSocket *tcpSocket; QString message; /存放從服務(wù)器接收到的字符串 quint16 blockS

11、ize; /存放文件的大小信息 添加私有槽函數(shù): private slots: void newCo nn ect(); / 連接服務(wù)器 主機: 端口號: void readMessage(); /接收數(shù)據(jù) if(blockSize=0) / 如果是剛開始接收數(shù)據(jù) void displayError(QAbstractSocket:SocketError); 4. 在 widget.cpp 文件中做更改。 (1 )在構(gòu)造函數(shù)中添加代碼: tcpSocket = new QTcpSocket(this); conn ect(tcpSocket,SIGNAL(readyRead(),this,SL

12、OT(readMessage(); conn ect(tcpSocket,SIGNAL(error(QAbstractSocket:SocketError), this,SLOT(displayError(QAbstractSocket:SocketError); 這里關(guān)聯(lián)了 tcpSocket 的兩個信號,當(dāng)有數(shù)據(jù)到來時發(fā)岀 readyRead() 信號,我們執(zhí)行讀取數(shù) 據(jù)的 readMessage() 函數(shù)。當(dāng)岀現(xiàn)錯誤時發(fā)岀 error()信號,我們執(zhí)行 displayError() 槽函數(shù) (2)實現(xiàn) newConnect() 函數(shù)。 void Widget:n ewC onn ect(

13、) blockSize = 0; / 初始化其為 0 tcpSocket-abort(); / 取消已有的連接 tcpSocket-c onn ectToHost(ui-hostLi neEdit-text(), ui-portLi neEdit-text().tol nt(); /連接到主機,這里從界面獲取主機地址和端口號 這個函數(shù)實現(xiàn)了連接到服務(wù)器,下面會在 連接”按鈕的單擊事件槽函數(shù)中調(diào)用這個函數(shù)。 (3 )實現(xiàn) readMessage() 函數(shù)。 void Widget:readMessage() QDataStream in (tcpSocket); in. setVersio n(

14、QDataStream:Qt_4_6); /顯示錯誤 /設(shè)置數(shù)據(jù)流版本,這里要和服務(wù)器端相同 /判斷接收的數(shù)據(jù)是否有兩字節(jié),也就是文件的大小信息 /如果有則保存到 blockSize 變量中,沒有則返回,繼續(xù)接收數(shù)據(jù) if(tcpSocket-bytesAvailable() blockSize; if(tcpSocket-bytesAvailable() message; /將接收到的數(shù)據(jù)存放到變量中 ui-messageLabel-setText(message); /顯示接收到的數(shù)據(jù) 這個函數(shù)實現(xiàn)了數(shù)據(jù)的接收,它與服務(wù)器端的發(fā)送函數(shù)相對應(yīng)。 首先我們要獲取文件的大小信息, 然后根據(jù)文件的

15、大小來判斷是否接收到了完整的文件。 (4)實現(xiàn) displayError() 函數(shù)。 void Widget:displayError(QAbstractSocket:SocketError) qDebug() errorStri ng(); / 輸岀錯誤信息 這里簡單的實現(xiàn)了錯誤信息的輸岀。 (5 )我們在 widget.ui 中進入 連接按鈕的單擊事件槽函數(shù),然后更改如下。 void Widget:on_pushButton_clicked() / 連接按鈕 n ewC onn ect(); / 請求連接 這里直接調(diào)用了 n ewCo nn ect() 函數(shù)。 5. 我們運行程序,同時運行

16、服務(wù)器程序,然后在 主機”后填入“l(fā)ocalhost ,”在 端口號”后填 入“ 6666” ,點擊 連接”按鈕,效果如下。 可以看到我們正確地接收到了數(shù)據(jù)。 因為服務(wù)器端和客戶端是在同一臺機子上運行的, 所以我這 里填寫了 主機”為“l(fā)ocalhost ”,如果你在不同的機子上運行,需要在 主機”后填寫其正確的 IP 地址。 到這里我們最簡單的 TCP 應(yīng)用程序就完成了, 在下一節(jié)我們將會對它進行擴展, 實現(xiàn)任意 文件的傳輸。 QT 網(wǎng)絡(luò)編程一 TCP 在上一節(jié)里我們使用 TCP 服務(wù)器發(fā)送一個字符串, 然后在 TCP 客戶端進行接收。 在這一節(jié)我們 重新寫一個客戶端程序和一個服務(wù)器程序,

17、這次我們讓客戶端進行文件的發(fā)送, 服務(wù)器進行文件 的接收。有了上一節(jié)的基礎(chǔ),這一節(jié)的內(nèi)容就很好理解了,注意一下幾個信號和槽的關(guān)聯(lián)即可。 當(dāng)然,我們這次要更深入了解一下數(shù)據(jù)的發(fā)送和接收的處理方法。 一、客戶端 這次我們先講解客戶端,在客戶端里我們與服務(wù)器進行連接,一旦連接成功,就會發(fā)出 conn ected() 信號,這時我們就進行文件的發(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ù)里我們

18、也要進行 qin t64 bytesWritte n; /已經(jīng)發(fā)送數(shù)據(jù)大小 相應(yīng)的處理。對于文件大小,這次我們使用了 qint64 ,它是 64 位的,可以表示一個很大的文 件了。 1. 同前一節(jié),我們新建工程,將工程命名為 “ tcpSender ”。注意添加 network 模塊。 2. 我們在 widget.ui 文件中將界面設(shè)計如下。 狀態(tài):等待打開文件號 打開 按送 這里 主機后的 Line Edit 的 objectName 為 hostLineEdit ; 端口 后的 Line Edit 的 objectName 為 portL in eEdit ;下面的 Progress Ba

19、r 的 objectName 為 clie ntProgressBar 其 value 屬性設(shè)為 0 ; 狀態(tài)” Label的 objetName 為 clientStatusLabel ; 打開按鈕的 objectName 為 openButton ;發(fā)送按鈕的 objectName 為 sendButton; 3. 在 widget.h 文件中進行更改。 (1 )添加頭文件 #i nclude (2 )添加 private 變量: QTcpSocket *tcpClie nt; QFile *localFile; /要發(fā)送的文件 數(shù)據(jù)。所以在發(fā)送函數(shù)里我們就要進行相應(yīng)的處理, 當(dāng)然,在服務(wù)

20、器的接收函數(shù)里我們也要進行 qin t64 bytesWritte n; /已經(jīng)發(fā)送數(shù)據(jù)大小 qint64 totalBytes; / 數(shù)據(jù)總大小qin t64 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 updateClie ntProg

21、ress(qi nt64); / 發(fā)送數(shù)據(jù),更新進度條 void displayError(QAbstractSocket:SocketError); / 顯示錯誤 void openFile(); / 打開文件 4. 在 widget.cpp 文件中進行更改。 添加頭文件:#i nclude (1 )在構(gòu)造函數(shù)中添加代碼: loadSize = 4*1024; totalBytes = 0; bytesWritte n = 0; bytesToWrite = 0; tcpClie nt = new QTcpSocket(this); conn ect(tcpClie nt,SIGNAL(co

22、 nn ected(),this,SLOT(startTra nsfer(); /當(dāng)連接服務(wù)器成功時,發(fā)岀 connected() 信號,我們開始傳送文件 conn ect(tcpClie nt,SIGNAL(bytesWritte n(qi nt64),this, SLOT(updateClie ntProgress(qi nt64); /當(dāng)有數(shù)據(jù)發(fā)送成功時,我們更新進度條 conn ect(tcpClie nt,SIGNAL(error(QAbstractSocket:SocketError),this, qin t64 bytesToWrite; /剩余數(shù)據(jù)大小 SLOT(display

23、Error(QAbstractSocket:SocketError); ui-se ndButt on-setE nabled(false); /開始使”發(fā)送 按鈕不可用 我們主要是進行了變量的初始化和幾個信號和槽函數(shù)的關(guān)聯(lián)。 (2 )實現(xiàn)打開文件函數(shù)。 void Widget:openFile() / 打開文件 fileName = QFileDialog:getOpe nFileName(this); if(!fileName.isEmpty() ui-se ndButt on-setE nabled(true); ui-clie ntStatusLabel- setText(tr( 打開

24、文件 %1 .arg(fileName); 該函數(shù)將在下面的 打開”按鈕單擊事件槽函數(shù)中調(diào)用。 (3 )實現(xiàn)連接函數(shù)。 void Widget:send() /連接到服務(wù)器,執(zhí)行發(fā)送 ui-se ndButt on-setE nabled(false); bytesWritte n = 0; /初始化已發(fā)送字節(jié)為 0 ui-clientStatusLabel- setText(tr( 連接中”); tcpClie nt-c onn ectToHost(ui-hostLi neEdit-text(), ui-portLi neEdit-text().tol nt(); 該函數(shù)將在發(fā)送”按鈕的單擊

25、事件槽函數(shù)中調(diào)用。 成功! ”) 連接 (4 )實現(xiàn)文件頭結(jié)構(gòu)的發(fā)送 void Widget:startTra nsfer() / 實現(xiàn)文件大小等信息的發(fā)送 localFile = new QFile(fileName); if(!localFile-ope n(QFile:Read On ly) qDebug() size(); / 文件總大小 QDataStream sen dOut(&outBlock,QIODevice:WriteO nl y); sen dOut.setVersio n(QDataStream:Qt_4_6); QStri ng curre ntFileNam

26、e = fileName.right(fileName.size()- fileName.last In dexOf(/)-1); sen dOut qin t64(0) qin t64(0) seek(0); sen dOutvvtotalBytesvvqi nt64(outBlock.size() - sizeof(qi nt64)*2); / 返回 outBolock 的開始,用實際的大小信息代替兩個 qint64(0)空間 bytesToWrite = totalBytes - tcpClie nt-write(outBlock); / 發(fā)送完頭數(shù)據(jù)后剩余數(shù)據(jù)的大小 ui-clie n

27、tStatusLabel-setText(tr(” 已連接); outBlock.resize(O); (5 )下面是更新進度條,也就是發(fā)送文件數(shù)據(jù)。 void Widget:updateClie ntProgress(qi nt64 n umBytes) / 更新進度條,實現(xiàn)文件的傳送 bytesWritte n += (int)n umBytes; /已經(jīng)發(fā)送數(shù)據(jù)的大小 if(bytesToWrite 0)/ 如果已經(jīng)發(fā)送了數(shù)據(jù) outBlock = localFile-read(qMi n( bytesToWrite,loadSize); /每次發(fā)送 loadSize 大小的數(shù)據(jù),這里設(shè)

28、置為 4KB,如果剩余的數(shù)據(jù)不足 4KB , /就發(fā)送剩余數(shù)據(jù)的大小 bytesToWrite -= (in t)tcpClie nt-write(outBlock); /發(fā)送完一次數(shù)據(jù)后還剩余數(shù)據(jù)的大小 outBlock.resize(0); /清空發(fā)送緩沖區(qū) else localFile-close(); / 如果沒有發(fā)送任何數(shù)據(jù),則關(guān)閉文件 ui-clie ntProgressBar-setMaximum(totalBytes); ui-clie ntProgressBar-setValue(bytesWritte n); /更新進度條 if(bytesWritte n = totalB

29、ytes) / 發(fā)送完畢 ui-clientStatusLabel- setText(tr( 傳送文件 %1 成功 ”).arg(fileName); localFile-close(); tcpClie nt-close(); (6 )實現(xiàn)錯誤處理函數(shù) void Widget:displayError(QAbstractSocket:SocketError) / 顯示錯誤 qDebug() errorStri ng(); tcpClie nt-close(); ui-clie ntProgressBar-reset(); ui-clientStatusLabel- setText(tr( 客

30、戶端就緒”); ui-se ndButt on-setE nabled(true); (7 )我們從 widget.ui 中分別進行 打開”按鈕和 發(fā)送”按鈕的單擊事件槽函數(shù), void Widget:on_ope nButt on _clicked() / 打開按鈕 ope nF ile(); void Widget:on_se ndButton_clicked() / 發(fā)送按鈕 sen d(); 5. 我們?yōu)榱耸钩绦蛑械闹形牟伙@示亂碼,在 添加頭文件:#i nclude 在 main 函數(shù)中添加代碼: QTextCodec:setCodecForTr(QTextCodec:codecFor

31、Locale(); 6. 運行程序,效果如下。然后更改如下。 mai n.cpp 文件中更改。 主機; 端口 : 狀態(tài):等鑄打開文件$ 打開 7. 程序整體思路分析。 我們設(shè)計好界面,然后按下 打開”按鈕,選擇我們要發(fā)送的文件,這時調(diào)用了 ope nF ile()函數(shù)。 然后我們點擊 發(fā)送”按鈕,調(diào)用 send()函數(shù),與服務(wù)器進行連接。當(dāng)連接成功時就會發(fā)岀 conn ected() 信號,這時就會執(zhí)行 startTra nsfer() 函數(shù),進行文件頭結(jié)構(gòu)的發(fā)送,當(dāng)發(fā)送成功 時就會發(fā)岀 bytesWritte n(qi nt64) 信號,這時我們執(zhí)行 updateClie ntProgres

32、s(qi nt64 numBytes) 進行文件數(shù)據(jù)的傳輸和進度條的更新。這里使用了一個 loadSize 變量,我們在構(gòu) 造函數(shù)中將其初始化為 4*1024 即 4 字節(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ā)岀 newC

33、o nn ectio n() 信號,然后我們便接受連接,開始接收數(shù)據(jù)。 1. 新建工程,名字為 “ tcpReceiver ” Tidget 回區(qū) 發(fā)送 0 2. 我們更改 widget.ui 文件,設(shè)計界面如下 其中 服務(wù)器端 ” Label 的 objectName 為 serverStatusLabel ;進度條 objectName 為 serverProgressBar ,設(shè)置其 value 屬性為 0 ;開始監(jiān)聽 為 startButton 。 效果如下 3. 更改 widget.h 文件的內(nèi)谷。 (1 )添加頭文件:#i nclude (2 )添加私有變量: QTcpServer

34、 tcpServer; QTcpSocket *tcpServerC onn ecti on; (3 )添加私有槽函數(shù):Progress Bar 的 按鈕的 objectName qin t64 totalBytes; qin t64 bytesReceived; qint64 fileNameSize; QStri ng fileName; QFile *localFile; /存放總大小信息 /已收到數(shù)據(jù)的大小 /文件名的大小信息 /存放文件名 /本地文件 /數(shù)據(jù)緩沖區(qū) private slots: void on _startButto n_ clicked。; void display

35、Error(QAbstractSocket:SocketError socketError); /顯示錯誤 4. 更改 widget.cpp 文件。 (1 )在構(gòu)造函數(shù)中添加代碼: totalBytes = 0; bytesReceived = 0; fileNameSize = 0; conn ect(&tcpServer,SIGNAL( newCo nn ectio n(),this, SLOT(acceptCo nn ectio n(); /當(dāng)發(fā)現(xiàn)新連接時發(fā)岀 newConnection() 信號 (2 )實現(xiàn) start()函數(shù)。 void Widget:start() / 開

36、始監(jiān)聽 ui-startButt on-setE nabled(false); bytesReceived =0; if(!tcpServer.liste n( QHostAddress:LocalHost,6666) qDebug() serverStatusLabel- setText(tr( 監(jiān)聽”); (3 )實現(xiàn)接受連接函數(shù)。 void Widget:acceptConnection() / 接受連接 tcpServerC onn ecti on = tcpServer. nextPe ndingConn ecti on(); conn ect(tcpServerCo nn ecti

37、o n,SIGNAL(readyRead(),this, SLOT(updateServerProgress(); conn ect(tcpServerC onn ecti on, SIGNAL(error(QAbstractSocket:SocketError),this, SLOT(displayError(QAbstractSocket:SocketError); ui-serverStatusLabel- setText(tr( 接受連接”); tcpServer.close(); (4 )實現(xiàn)更新進度條函數(shù)。 void Widget:updateServerProgress() /

38、更新進度條,接收數(shù)據(jù) QDataStream in (tcpServerC onn ecti on); in. setVersio n(QDataStream:Qt_4_6); if(bytesReceived bytesAvailable() = sizeof( qin t64)*2) & (fileNameSize = 0) /接收數(shù)據(jù)總大小信息和文件名大小信息 /來的頭文件信 in totalBytes fileNameSize; bytesReceived += sizeof(qi nt64) * 2; if(tcpServerC onn ectio n-bytesAvaila

39、ble() = fileNameSize) & (fileNameSize != 0) /接收文件名,并建立文件 in fileName; ui-serverStatusLabel- setText(tr( 接收文件 .arg(fileName); bytesReceived += fileNameSize; localFile = new QFile(fileName); if(!localFile-ope n(QFile:Write On ly) qDebug() “ open file error! ”; return; else return; if(bytesReceived

40、 bytesAvailable(); in Block = tcpServerC onn ectio n-readAll(); localFile-write(i nBlock); in Block.resize(O); %1 ”) ui-serverProgressBar-setMaximum(totalBytes); ui-serverProgressBar-setValue(bytesReceived); /更新進度條 if(bytesReceived = totalBytes) /接收數(shù)據(jù)完成時 tcpServerC onn ectio n-close(); localFile-clo

41、se(); ui-startButt on-setE nabled(true); ui-serverStatusLabel- setText(tr( 接收文件 %1 成功! ”) .arg(fileName); (5 )錯誤處理函數(shù)。 void Widget:displayError(QAbstractSocket:SocketError) / 錯誤處理 qDebug() errorStr in g(); tcpServerC onn ectio n-close(); ui-serverProgressBar-reset(); ui-serverStatusLabel- setText(tr(

42、 服務(wù)端就緒”); ui-startButt on-setE nabled(true); (6 )我們在 widget.ui 中進入開始監(jiān)聽按鈕的單擊事件槽函數(shù),更改如下。 void Widget:on_startButton_clicked() / 開始監(jiān)聽按鈕 start(); 5.我們?yōu)榱耸钩绦蛑械闹形牟伙@示亂碼,在 mai n.cpp 文件中更改。 添加頭文件:#i nclude 在 main 函數(shù)中添加代碼: QTextCodec:setCodecForTr(QTextCodec:codecForLocale(); 6.運行程序,并同時運行 tcpSender 程序,效果如下。 我們

43、先在服務(wù)器端按下 開始監(jiān)聽”按鈕,然后在客戶端輸入主機地址和端口號, 然后打開要發(fā)送 的文件,點擊 發(fā)送”按鈕進行發(fā)送。 在這兩節(jié)里我們介紹了 TCP的應(yīng)用,可以看到服務(wù)器端和客戶度端都可以當(dāng)做發(fā)送端或者 接收端,而且數(shù)據(jù)的發(fā)送與接收只要使用相對應(yīng)的協(xié)議即可, 它是可以根據(jù)用戶的需要來進行編 程的,沒有固定的格式。 QT 網(wǎng)絡(luò)編程-UDP 這一節(jié)講述 UDP 編程的知識。UDP ( User Datagram Protocol 即用戶數(shù)據(jù)報協(xié)議)是一個輕 量級的,不可靠的,面向數(shù)據(jù)報的無連接協(xié)議。對于 UDP 我們不再進行過多介紹,如果你對 UDP 不是很了解,而且不知道它有什么用,那么我們這

44、里就舉個簡單的例子:我們現(xiàn)在幾乎每 個人都使用的騰訊 QQ,其聊天時就是使用 UDP協(xié)議進行消息發(fā)送的。就像 QQ 那樣,當(dāng)有很 多用戶,發(fā)送的大部分都是短消息, 要求能及時響應(yīng),并且對安全性要求不是很高的情況下使用 UDP 協(xié)議。 在 Qt 中提供了 QUdpSocket 類來進行 UDP數(shù)據(jù)報(datagrams )的發(fā)送和接收。這 里我們還要了解一個名詞 Socket,也就是常說的 套接字”。Socket 簡單地說,就是一個 IP地址加一個 port 端口。因為我們要傳輸數(shù)據(jù),就要知道往哪個機子上傳送,而 IP 地址確定了 一臺主機,但是這臺機子上可能運行著各種各樣的網(wǎng)絡(luò)程序, 我們要往

45、哪個程序中發(fā)送呢?這時 就要使用一個端口來指定 UDP 程序。所以說,Socket 指明了數(shù)據(jù)報傳輸?shù)穆窂健?下面我們將編寫兩個程序,一個用來發(fā)送數(shù)據(jù)報,可以叫做客戶端;另一個用來接收數(shù)據(jù)報, 可 以叫做服務(wù)器端,它們均應(yīng)用 UDP 協(xié)議。這樣也就構(gòu)成了所謂的 C/S (客戶端/服務(wù)器)編程 模型。我們會在編寫程序的過程中講解一些相關(guān)的網(wǎng)絡(luò)知識。 (一)發(fā)送端(客戶端) 1. 我們新建 Qt4 Gui Application ,工程名為 “ udpSender ”,選中 QtNetwork 模塊, Base class 選擇 QWidget 。 2. 我們在 widget.ui 文件中,往界面

46、上添加一個 Push Butt on ,更改其顯示文本為 開始廣 播”,然后進入其單擊事件槽函數(shù)。 3. 我們在 widget.h 文件中更改。 添加頭文件:#i nclude 添加 private 私有對象:QUdpSocket *sender; 4. 我們在 widget.cpp 中進行更改。 在構(gòu)造函數(shù)中添加: sen der = new QUdpSocket(this); 更改 開始廣播”按鈕的單擊事件槽函數(shù): 地址加一個 port 端口。因為我們要傳輸數(shù)據(jù),就要知道往哪個機子上傳送,而 IP 地址確定了 void Widget: on _pushButt on _clicked()/

47、發(fā)送廣播 QByteArray datagram = hello world! Base class 選擇 QWidget 。此時工程文件列表中應(yīng)包含兩個工程,如下圖 sen der-writeDatagram(datagram.data(),datagram.size(), QHostAddress:Broadcast,45454); 這里我們定義了一個 QByteArray 類型的數(shù)據(jù)報 datagram ,其內(nèi)容為 “hello world! ”。然后 我們使用 QUdpSocket 類的 writeDatagram() 函數(shù)來發(fā)送數(shù)據(jù)報,這個函數(shù)有四個參數(shù),分 別是數(shù)據(jù)報的內(nèi)容,數(shù)據(jù)報

48、的大小,主機地址和端口號。對于數(shù)據(jù)報的大小, 它根據(jù)平臺的不同 而不同,但是這里建議不要超過 512 字節(jié)。這里我們使用了廣播地址 QHostAddress:Broadcast ,這樣就可以同時給網(wǎng)絡(luò)中所有的主機發(fā)送數(shù)據(jù)報了。 對于端口號, 它是可以隨意指定的,但是一般 1024 以下的端口號通常屬于保留端口號,所以我們最好使用 大于 1024 的端口,最大為 65535。我們這里使用了 45454 這個端口號,一定要注意,在下 面要講的服務(wù)器程序中,也要使用相同的端口號。 5. 發(fā)送端就這么簡單,我們運行程序,效果如下。 QByteArray datagram = hello world!

49、Base class 選擇 QWidget 。此時工程文件列表中應(yīng)包含兩個工程,如下圖 (二)接收端(服務(wù)器端) 1. 我們新建 Qt4 Gui ,工程名為 “ udpReceiver ”,選中 QtNetwork 模塊, Fj ME Fl ITflt udpE-iCii ver 囲 udpEecivr. pro Forms N wi dget. HL - $ Headers 倉 widest, h -呂 Sources main, epp wi d?et. epp - 應(yīng) udpS nd r 劉 ndpSendtr. prt* - j Forms 團 wi dget. HL I-占 Head

50、ers i wi t. h I- 口 Sources Kai R. epp r wi dget. epp 2. 我們在 udpReceiver 工程中的 widget.ui 文件中,向界面上添加一個 Label 部件,更改 其顯示文本為 “等待接收數(shù)據(jù)! ”,效果如下。 3. 我們在 udpReceiver 工程中的 widget.h 文件中更改。 添加頭文件:#i nclude 添加 private 私有對象:QUdpSocket *receiver; 添加私有槽函數(shù): private slots: void processPe ndin gDatagram(); 在構(gòu)造函數(shù)中: recei

51、ver = new QUdpSocket(this); receiver-b in d(45454,QUdpSocket:ShareAddress); 4.我們在 udpReceiver 工程中的 widget.cpp 文件中更改。 conn ect(receiver,SIGNAL(readyRead(),this,SLOT(processPe ndin gDatagram(); 我們在構(gòu)造函數(shù)中將 receiver 綁定到 45454 端口,這個端口就是上面發(fā)送端設(shè)置的端口,二 者必須一樣才能保證接收到數(shù)據(jù)報。 我們這里使用了綁定模式 QUdpSocket:ShareAddress 它表明其

52、他服務(wù)也可以綁定到這個端口上。因為當(dāng) receiver 發(fā)現(xiàn)有數(shù)據(jù)報到達時就會發(fā)岀 readyRead() 信號,所以我們將其和我們的數(shù)據(jù)報處理函數(shù)相關(guān)聯(lián)。 數(shù)據(jù)報處理槽函數(shù)實現(xiàn)如下: void Widget:processPe ndi ngDatagram() / 處理等待的數(shù)據(jù)報 while(receiver-hasPendingDatagrams() / 擁有等待的數(shù)據(jù)報 QByteArray datagram; / 擁于存放接收的數(shù)據(jù)報 datagram.resize(receiver-pe ndin gDatagramSize(); /讓 datagram 的大小為等待處理的數(shù)據(jù)報的

53、大小,這樣才能接收到完整的數(shù)據(jù) receiver-readDatagram(datagram.data(),datagram.size(); /接收數(shù)據(jù)報,將其存放到 datagram 中 ui-label-setText(datagram); /將數(shù)據(jù)報內(nèi)容顯示岀來 5.我們在工程列表中 udpReceiver 工程上點擊鼠標(biāo)右鍵,在彈岀的菜單上選擇 run 菜單來 運行該工程 鼻兮日X Build Project udpHec Rebuild Project HdpR Clean Project !TudpRecFr Tt udpRecsi S * 二呂 Sour 電m i 0 SndpS

54、en 判 ndpS 嚴(yán) 到 Set Build Confi gurati Set Rua Configwati on Hz仕也.u* -Headers ( ( j tti dg&t. b 二曲 Sources 直 mAirt rmn 如果是在 linux下,你可能還需要關(guān)閉防火墻。 7.我們同時再運行 udpSender 程序。然后點擊其上的 “發(fā)送廣播按鈕,這時會在 udpReceiver 上顯示數(shù)據(jù)報的內(nèi)容。效果如下。 Close Project udpRec Add New . Adii Exi sting Files. . r 6.第一次運行該程序時,系統(tǒng)可能會提示警告,我們選

55、擇 解除阻止 可以看到,UDP的應(yīng)用是很簡單的。 我們只需要在發(fā)送端執(zhí)行 writeDatagram() 函數(shù)進行數(shù)據(jù) 報的發(fā)送,然后在接收端綁定端口,并關(guān)聯(lián) readyRead() 信號和數(shù)據(jù)報處理函數(shù)即可。 QT 網(wǎng)絡(luò)編程-獲取本機 IP 對于IP地址,其實,會上網(wǎng)的人都應(yīng)該聽說過它。如果你實在很不了解它, 那么我們簡單的說: IP 即 In ternet Protocol (網(wǎng)絡(luò)之間互聯(lián)的協(xié)議),協(xié)議就是規(guī)則,地球人都用一樣的規(guī)則, 所以我們可以訪問全球任何的網(wǎng)站;而 IP 地址就是你聯(lián)網(wǎng)時分配給你機子的一個地址。如果把 網(wǎng)絡(luò)比喻成地圖,那 IP 地址就像地圖上的經(jīng)緯度一樣,它確定了你的

56、主機在網(wǎng)絡(luò)中的位置。其 實知道我們以后要用 IP 地址來代表網(wǎng)絡(luò)中的一臺計算機就夠了。 ( A/ 不一定科學(xué)但是很直白 的表述) 下面我們就講解如何獲取自己電腦的 IP 地址以及其他網(wǎng)絡(luò)信息。這一節(jié)中,我們會涉及到網(wǎng)絡(luò) 模塊(QtNetwork Module )中的 QHostInfo , QHostAddress , QNetworkInterface 和 QNetworkAddressE ntry 等幾個類。下面是詳細(xì)內(nèi)容。 我們新建 Qt4 Gui Application 工程,工程名為 myIP ,選中 QtNetwork 模塊,Base class 選擇 QWidget 。 Vidc

57、et Vidcet h lla 甘arid.! 我們在 widget.h 文件中包含頭文件: #include 1. 使用 QHostInfo 獲取主機名和 IP 地址。 (1 )獲取主機名。 我們在 widget.cpp 文件中的構(gòu)造函數(shù)中添加代碼: QStri ng localHostName = QHostI nfo:localHostName(); qDebug() ” localHostName: “ localHostName; 這里我們使用了 QHostInfo 類的 localHostName 類來獲取本機的計算機名稱。 運行程序,在下面的輸出欄里的信息如下: mylP B S

58、tarting F: myIFdebuer*yIF. ex . r. loealKostUairie: yafeilinux: 可以看到,這里獲取了計算機名。我們可以在桌面上 我的電腦”圖標(biāo)上點擊鼠標(biāo)右鍵, 屬性”菜單,查看 計算機名”一項,和我們的輸出結(jié)果是一樣的,如下圖。ApplicatLOIL Output 然后選擇 系統(tǒng)屬性 (2 )獲取本機的 IP 地址。 我們繼續(xù)在構(gòu)造函數(shù)中添加代碼: QHost Info info = QHostl nfo:fromName(localHostName); qDebug() ” IP Address: 我們應(yīng)用 QHostInfo 類的 from

59、Name() 函數(shù),使用上面獲得的主機名為參數(shù), 來獲取本機的信 息。然后再利用 QHostInfo 類的 addresses() 函數(shù),獲取本機的所有 IP 地址信息。運行程序, 輸出信息如下: myIP Startiiig F:myIFdebiULgmyIF_ &xe.,. localHostHsm&: eilinux IP Address: HostAddresE C192- 1S8.1 100) in fo.addresses(); Application Output 在我這里只有一條 IP 地址。但是,在其他系統(tǒng)上,可能岀現(xiàn)多條 IP 地址,其中可能包含了 IPv4 和 IPv6 的地址,一般我們需要使用 IPv4 的地址,所以我們可以只輸岀 IPv4 的地址。 我們繼續(xù)添加代碼: foreach(QHostAddress addressnfo.addresses() if(tocol() = QAbstractSocket:IPv4Protocol) qDebug() address.toString(); 因為 IP 地址由 QHostAddress 類來管理,所以我們可以使用該類來獲取一條 IP 地址,然后使 用該類的 protocol。 函數(shù)來判斷其是否為 IPv4 地址。如果是

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論