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

下載本文檔

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

文檔簡介

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

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

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

4、QTcpServer *tcpServer; 添加私有槽函數(shù): private slots: void sen dMessage(); 4. 在 widget.cpp 文件中進(jìn)行更改。 在其構(gòu)造函數(shù)中添加代碼: tcpServer = new QTcpServer(this); if(!tcpServer-liste n(QHostAddress:LocalHost,6666) /監(jiān)聽本地主機(jī)的 6666 端口,如果岀錯(cuò)就輸岀錯(cuò)誤信息,并關(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ù)進(jìn)行監(jiān)聽,然后關(guān)聯(lián)了 newConnection() 和我們自己的 sen dMessage() 函數(shù)。 下面我們實(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ù)成功后,顯示提示 這個(gè)是數(shù)據(jù)發(fā)送函數(shù),我們主要介紹兩點(diǎn): (1)為了保證在客戶端能接收到完整的文件,我們都在數(shù)據(jù)流的最開始寫入完整文件的大小信 息,這樣客戶端就可以根據(jù)大小信息來判斷是否接受到了完整的文件。 而在服務(wù)器端,我們在發(fā) 送數(shù)據(jù)時(shí)就要首先發(fā)送實(shí)際文件的大小信息, 但是,文件的大小一開始是無法預(yù)知的, 所以我們 先使用了 out(quint16) 0; 在 block 的開始添加了一個(gè) quint16 大小的空間,也就是兩字 節(jié)的空間,它用于后面放置文件的大小信息。然后 outs

8、eek(0); 返回到 block 的開始, 加入實(shí)際的文件大小信息,也就是后面的代碼,它是實(shí)際文件的大小: 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ā)送完成時(shí)就會(huì)斷開連接,這 時(shí)就會(huì)發(fā)岀 disc onn ected() 信號,而最后調(diào)用 deleteLater() 函數(shù)保證在關(guān)閉連接后刪除該套 接字 clientConnection 。 5. 這樣服務(wù)器的程序就完成了,我們先運(yùn)行一下程序。 、客戶端。 我們在客戶端程序中向服務(wù)器發(fā)送連接請求,當(dāng)連接成功時(shí)接收服務(wù)器發(fā)送的數(shù)據(jù)。 1.我們新建 Qt4 Gui Application ,工程名為 “ tcpCIient ,選中 QtNetwork 模塊,Base class 選擇 QWidget 。 2,我們在 widget

10、.ui 中添加幾個(gè)標(biāo)簽 Label和兩個(gè) Line Edit 以及一個(gè)按鈕 Push Butt on 收至1的信息. 其中 主機(jī)后的 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ù)器 主機(jī): 端口號: 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 的兩個(gè)信號,當(dāng)有數(shù)據(jù)到來時(shí)發(fā)岀 readyRead() 信號,我們執(zhí)行讀取數(shù) 據(jù)的 readMessage() 函數(shù)。當(dāng)岀現(xiàn)錯(cuò)誤時(shí)發(fā)岀 error()信號,我們執(zhí)行 displayError() 槽函數(shù) (2)實(shí)現(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(); /連接到主機(jī),這里從界面獲取主機(jī)地址和端口號 這個(gè)函數(shù)實(shí)現(xiàn)了連接到服務(wù)器,下面會(huì)在 連接”按鈕的單擊事件槽函數(shù)中調(diào)用這個(gè)函數(shù)。 (3 )實(shí)現(xiàn) readMessage() 函數(shù)。 void Widget:readMessage() QDataStream in (tcpSocket); in. setVersio n(

14、QDataStream:Qt_4_6); /顯示錯(cuò)誤 /設(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ù) 這個(gè)函數(shù)實(shí)現(xiàn)了數(shù)據(jù)的接收,它與服務(wù)器端的發(fā)送函數(shù)相對應(yīng)。 首先我們要獲取文件的大小信息, 然后根據(jù)文件的

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

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

17、這次我們讓客戶端進(jìn)行文件的發(fā)送, 服務(wù)器進(jìn)行文件 的接收。有了上一節(jié)的基礎(chǔ),這一節(jié)的內(nèi)容就很好理解了,注意一下幾個(gè)信號和槽的關(guān)聯(lián)即可。 當(dāng)然,我們這次要更深入了解一下數(shù)據(jù)的發(fā)送和接收的處理方法。 一、客戶端 這次我們先講解客戶端,在客戶端里我們與服務(wù)器進(jìn)行連接,一旦連接成功,就會(huì)發(fā)出 conn ected() 信號,這時(shí)我們就進(jìn)行文件的發(fā)送。 在上一節(jié)我們已經(jīng)看到, 發(fā)送數(shù)據(jù)時(shí)我們先發(fā)送了數(shù)據(jù)的大小信息。 這一次,我們要先發(fā)送文件 的總大小,然后文件名長度,然后是文件名,這三部分我們合稱為文件頭結(jié)構(gòu),最后再發(fā)送文件 數(shù)據(jù)。所以在發(fā)送函數(shù)里我們就要進(jìn)行相應(yīng)的處理, 當(dāng)然,在服務(wù)器的接收函數(shù)里我們

18、也要進(jìn)行 qin t64 bytesWritte n; /已經(jīng)發(fā)送數(shù)據(jù)大小 相應(yīng)的處理。對于文件大小,這次我們使用了 qint64 ,它是 64 位的,可以表示一個(gè)很大的文 件了。 1. 同前一節(jié),我們新建工程,將工程命名為 “ tcpSender ”。注意添加 network 模塊。 2. 我們在 widget.ui 文件中將界面設(shè)計(jì)如下。 狀態(tài):等待打開文件號 打開 按送 這里 主機(jī)后的 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 文件中進(jìn)行更改。 (1 )添加頭文件 #i nclude (2 )添加 private 變量: QTcpSocket *tcpClie nt; QFile *localFile; /要發(fā)送的文件 數(shù)據(jù)。所以在發(fā)送函數(shù)里我們就要進(jìn)行相應(yīng)的處理, 當(dāng)然,在服務(wù)

20、器的接收函數(shù)里我們也要進(jìn)行 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ù),更新進(jìn)度條 void displayError(QAbstractSocket:SocketError); / 顯示錯(cuò)誤 void openFile(); / 打開文件 4. 在 widget.cpp 文件中進(jìn)行更改。 添加頭文件:#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ù)器成功時(shí),發(fā)岀 connected() 信號,我們開始傳送文件 conn ect(tcpClie nt,SIGNAL(bytesWritte n(qi nt64),this, SLOT(updateClie ntProgress(qi nt64); /當(dāng)有數(shù)據(jù)發(fā)送成功時(shí),我們更新進(jìn)度條 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ā)送 按鈕不可用 我們主要是進(jìn)行了變量的初始化和幾個(gè)信號和槽函數(shù)的關(guān)聯(lián)。 (2 )實(shí)現(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 )實(shí)現(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 )實(shí)現(xiàn)文件頭結(jié)構(gòu)的發(fā)送 void Widget:startTra nsfer() / 實(shí)現(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 的開始,用實(shí)際的大小信息代替兩個(gè) qint64(0)空間 bytesToWrite = totalBytes - tcpClie nt-write(outBlock); / 發(fā)送完頭數(shù)據(jù)后剩余數(shù)據(jù)的大小 ui-clie n

27、tStatusLabel-setText(tr(” 已連接); outBlock.resize(O); (5 )下面是更新進(jìn)度條,也就是發(fā)送文件數(shù)據(jù)。 void Widget:updateClie ntProgress(qi nt64 n umBytes) / 更新進(jìn)度條,實(shí)現(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); /更新進(jìn)度條 if(bytesWritte n = totalB

29、ytes) / 發(fā)送完畢 ui-clientStatusLabel- setText(tr( 傳送文件 %1 成功 ”).arg(fileName); localFile-close(); tcpClie nt-close(); (6 )實(shí)現(xiàn)錯(cuò)誤處理函數(shù) void Widget:displayError(QAbstractSocket:SocketError) / 顯示錯(cuò)誤 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 中分別進(jìn)行 打開”按鈕和 發(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. 運(yùn)行程序,效果如下。然后更改如下。 mai n.cpp 文件中更改。 主機(jī); 端口 : 狀態(tài):等鑄打開文件$ 打開 7. 程序整體思路分析。 我們設(shè)計(jì)好界面,然后按下 打開”按鈕,選擇我們要發(fā)送的文件,這時(shí)調(diào)用了 ope nF ile()函數(shù)。 然后我們點(diǎn)擊 發(fā)送”按鈕,調(diào)用 send()函數(shù),與服務(wù)器進(jìn)行連接。當(dāng)連接成功時(shí)就會(huì)發(fā)岀 conn ected() 信號,這時(shí)就會(huì)執(zhí)行 startTra nsfer() 函數(shù),進(jìn)行文件頭結(jié)構(gòu)的發(fā)送,當(dāng)發(fā)送成功 時(shí)就會(huì)發(fā)岀 bytesWritte n(qi nt64) 信號,這時(shí)我們執(zhí)行 updateClie ntProgres

32、s(qi nt64 numBytes) 進(jìn)行文件數(shù)據(jù)的傳輸和進(jìn)度條的更新。這里使用了一個(gè) loadSize 變量,我們在構(gòu) 造函數(shù)中將其初始化為 4*1024 即 4 字節(jié),它的作用是,我們將整個(gè)大的文件分成很多小的部 分進(jìn)行發(fā)送,每部分為 4 字節(jié)。而當(dāng)連接出現(xiàn)問題時(shí)就會(huì)發(fā)出 error(QAbstractSocket:SocketError) 信號,這時(shí)就會(huì)執(zhí)行 displayError() 函數(shù)。對于程序 中其他細(xì)節(jié)我們就不再分析,希望大家能自己編程研究一下。 二、服務(wù)器端。 我們在服務(wù)器端進(jìn)行數(shù)據(jù)的接收。 服務(wù)器端程序是很簡單的, 我們開始進(jìn)行監(jiān)聽,一旦發(fā)現(xiàn)有連 接請求就發(fā)岀 newC

33、o nn ectio n() 信號,然后我們便接受連接,開始接收數(shù)據(jù)。 1. 新建工程,名字為 “ tcpReceiver ” Tidget 回區(qū) 發(fā)送 0 2. 我們更改 widget.ui 文件,設(shè)計(jì)界面如下 其中 服務(wù)器端 ” Label 的 objectName 為 serverStatusLabel ;進(jìn)度條 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); /顯示錯(cuò)誤 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)新連接時(shí)發(fā)岀 newConnection() 信號 (2 )實(shí)現(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 )實(shí)現(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 )實(shí)現(xiàn)更新進(jìn)度條函數(shù)。 void Widget:updateServerProgress() /

38、更新進(jìn)度條,接收數(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); /更新進(jìn)度條 if(bytesReceived = totalBytes) /接收數(shù)據(jù)完成時(shí) tcpServerC onn ectio n-close(); localFile-clo

41、se(); ui-startButt on-setE nabled(true); ui-serverStatusLabel- setText(tr( 接收文件 %1 成功! ”) .arg(fileName); (5 )錯(cuò)誤處理函數(shù)。 void Widget:displayError(QAbstractSocket:SocketError) / 錯(cuò)誤處理 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 中進(jìn)入開始監(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.運(yùn)行程序,并同時(shí)運(yùn)行 tcpSender 程序,效果如下。 我們

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

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

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

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

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

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

49、Base class 選擇 QWidget 。此時(shí)工程文件列表中應(yīng)包含兩個(gè)工程,如下圖 (二)接收端(服務(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 團(tuán) wi dget. HL I-占 Head

50、ers i wi t. h I- 口 Sources Kai R. epp r wi dget. epp 2. 我們在 udpReceiver 工程中的 widget.ui 文件中,向界面上添加一個(gè) 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 端口,這個(gè)端口就是上面發(fā)送端設(shè)置的端口,二 者必須一樣才能保證接收到數(shù)據(jù)報(bào)。 我們這里使用了綁定模式 QUdpSocket:ShareAddress 它表明其

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

53、大小,這樣才能接收到完整的數(shù)據(jù) receiver-readDatagram(datagram.data(),datagram.size(); /接收數(shù)據(jù)報(bào),將其存放到 datagram 中 ui-label-setText(datagram); /將數(shù)據(jù)報(bào)內(nèi)容顯示岀來 5.我們在工程列表中 udpReceiver 工程上點(diǎn)擊鼠標(biāo)右鍵,在彈岀的菜單上選擇 run 菜單來 運(yùn)行該工程 鼻兮日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.我們同時(shí)再運(yùn)行 udpSender 程序。然后點(diǎn)擊其上的 “發(fā)送廣播按鈕,這時(shí)會(huì)在 udpReceiver 上顯示數(shù)據(jù)報(bào)的內(nèi)容。效果如下。 Close Project udpRec Add New . Adii Exi sting Files. . r 6.第一次運(yùn)行該程序時(shí),系統(tǒng)可能會(huì)提示警告,我們選

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

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

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

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

59、Name() 函數(shù),使用上面獲得的主機(jī)名為參數(shù), 來獲取本機(jī)的信 息。然后再利用 QHostInfo 類的 addresses() 函數(shù),獲取本機(jī)的所有 IP 地址信息。運(yùn)行程序, 輸出信息如下: 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(); 因?yàn)?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)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論