下載本文檔
版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年機(jī)床設(shè)備交易協(xié)議范本
- DB3502∕T 085-2022 三角梅盆栽生產(chǎn)技術(shù)規(guī)程
- 2024年房產(chǎn)租賃協(xié)議格式
- 2024年專項(xiàng)融資墊付服務(wù)協(xié)議模板
- 2024-2025學(xué)年廣東省江門九中九年級(上)期中數(shù)學(xué)試卷
- 2024三方銷售業(yè)務(wù)協(xié)議條款集
- 2024年餐廳股份出售協(xié)議模板
- 辦公物資采購協(xié)議范本(2024年)
- 2024年專業(yè)家裝合作協(xié)議詳例
- 2024物業(yè)學(xué)校綜合服務(wù)協(xié)議
- 蘇教版五年級上冊數(shù)學(xué)試題-第一、二單元 測試卷【含答案】
- 發(fā)揮產(chǎn)業(yè)工會(huì)作用的實(shí)施方案
- 科捷物流介紹(中文版)ppt課件
- 軍事地形學(xué)地形圖基本知識
- 2022版義務(wù)教育(生物學(xué))課程標(biāo)準(zhǔn)(含2022年修訂和新增部分)
- 六年級綜合實(shí)踐活動(dòng)課件-珍愛生命遠(yuǎn)離毒品 全國通用(共24張PPT)
- 建設(shè)工程竣工消防驗(yàn)收記錄表(DOC36頁)
- 沉井專項(xiàng)施工方案DOC
- 切削力計(jì)算參考模板
- 一年級海洋教育教案
- 聚氨酯硬泡沫配方及計(jì)算
評論
0/150
提交評論