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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

[Qt教程] 第37篇 网络(七)TCP(一)

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

[Qt教程]?第37篇 網絡(七)TCP(一)

樓主 ?發表于 2013-9-6 15:44:45?|?查看: 398|?回復: 1
TCP?(一)

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

導語

TCPTransmissionControl Protocol,傳輸控制協議。與UDP不同,它是面向連接和數據流的可靠傳輸協議。也就是說,它能使一臺計算機上的數據無差錯的發往網絡上的其他計算機,所以當要傳輸大量數據時,我們選用TCP協議。 TCP協議的程序使用的是客戶端/服務器(C/S)模式,在Qt中提供了QTcpSocket類來編寫客戶端程序,使用QTcpServer類編寫服務器端程序。我們在服務器端進行端口的**,一旦發現客戶端的連接請求,就會發出newConnection()信號,可以關聯這個信號到我們自己的槽進行數據的發送。而在客戶端,一旦有數據到來就會發出readyRead()信號,可以關聯此信號進行數據的接收。其實,在程序中最難理解的地方就是程序的發送和接收了,為了讓大家更好的理解,我們在這一節只是講述一個傳輸簡單的字符串的例子,在下一節再進行擴展,實現任意文件的傳輸。


環境:Windows Xp + Qt 4.8.5+Qt Creator2.8.0


目錄

一、服務器端 二、客戶端



正文

一、服務器端
在服務器端的程序中,我們**本地主機的一個端口,這里使用6666,然后關聯newConnection()信號與自己寫的sendMessage()槽。就是說一旦有客戶端的連接請求,就會執行sendMessage()函數,在這個函數里我們發送一個簡單的字符串。

1.新建QtGui應用 項目名為tcpServer,基類選擇QWidget,類名為Widget。完成后打開項目文件tcpServer.pro并添加一行代碼:QT += network ,然后保存該文件。

2.widget.ui的設計區添加一個Label,更改其顯示文本為“等待連接”,然后更改其objectNamestatusLabel,用于顯示一些狀態信息。

3.widget.h文件中做以下更改。 添加頭文件:#include?<QtNetWork> 添加private對象:QTcpServer?*tcpServer; 添加私有槽: private?slots: void sendMessage();

4.在widget.cpp文件中進行更改。 在其構造函數中添加代碼: tcpServer = new QTcpServer(this); if(!tcpServer->listen(QHostAddress::LocalHost,6666)) {??//**本地主機的6666端口,如果出錯就輸出錯誤信息,并關閉 ? ??qDebug() << tcpServer->errorString(); ? ??close(); } //連接信號和相應槽函數 connect(tcpServer,SIGNAL(newConnection()),this,SLOT(sendMessage())); 我們在構造函數中使用tcpServerlisten()函數進行**,然后關聯了newConnection()和我們自己的sendMessage()函數。 下面我們實現sendMessage()函數。
void?Widget::sendMessage() { ? ??//用于暫存我們要發送的數據 ? ??QByteArray?block; ? ? ? ??//使用數據流寫入數據 ? ??QDataStream?out(&block,QIODevice::WriteOnly); ? ? ? ??//設置數據流的版本,客戶端和服務器端使用的版本要相同 ? ??out.setVersion(QDataStream::Qt_4_6); ? ? ? ??out<<(quint16)?0; ? ??out<<tr("hello?Tcp!!!"); ? ??out.device()->seek(0); ? ??out<<(quint16)?(block.size()?-?sizeof(quint16)); ? ? ? ??//我們獲取已經建立的連接的子套接字 ? ??QTcpSocket?*clientConnection?=?tcpServer->nextPendingConnection(); ? ? ? ??connect(clientConnection,SIGNAL(disconnected()),clientConnection, ? ?? ?? ???SLOT(deleteLater())); ? ??clientConnection->write(block); ? ??clientConnection->disconnectFromHost(); ? ? ? ??//發送數據成功后,顯示提示 ? ??ui->statusLabel->setText("send?message?successful!!!"); } 這個是數據發送函數,我們主要介紹兩點:
(1)為了保證在客戶端能接收到完整的文件,我們都在數據流的最開始寫入完整文件的大小信息,這樣客戶端就可以根據大小信息來判斷是否接受到了完整的文件。而在服務器端,在發送數據時就要首先發送實際文件的大小信息,但是,文件的大小一開始是無法預知的,所以這里先使用了out<<(quint16) 0;在block的開始添加了一個quint16大小的空間,也就是兩字節的空間,它用于后面放置文件的大小信息。然后out<<tr("hello Tcp!!!");輸入實際的文件,這里是字符串。當文件輸入完成后我們再使用out.device()->seek(0);返回到block的開始,加入實際的文件大小信息,也就是后面的代碼,它是實際文件的大小:out<<(quint16) (block.size() - sizeof(quint16));
2)在服務器端我們可以使用tcpServernextPendingConnection()函數來獲取已經建立的連接的Tcp套接字,使用它來完成數據的發送和其它操作。比如這里,我們關聯了disconnected()信號和deleteLater()槽,然后我們發送數據 clientConnection->write(block); 然后是clientConnection->disconnectFromHost(); 它表示當發送完成時就會斷開連接,這時就會發出disconnected()信號,而最后調用deleteLater()函數保證在關閉連接后刪除該套接字clientConnection
5.這樣服務器的程序就完成了,可以先運行一下程序。



二、客戶端 我們在客戶端程序中向服務器發送連接請求,當連接成功時接收服務器發送的數據。

1.新建Qt Gui應用, 項目名tcpClient,基類選擇QWidget,類名為Widget。完成后打開項目文件tcpClient.pro并添加一行代碼:QT += network ,然后保存該文件。

2.我們在widget.ui中添加幾個標簽Label和兩個Line Edit以及一個按鈕Push Button。設計效果如下圖所示。





其中“主機”后的LineEditobjectNamehostLineEdit,“端口號”后的為portLineEdit “收到的信息”標簽的objectNamemessageLabel?

3.widget.h文件中做更改。 添加頭文件:#include?<QtNetwork> 添加private變量: QTcpSocket?*tcpSocket; QString?message;??//存放從服務器接收到的字符串 quint16blockSize; //存放文件的大小信息 添加私有槽: private?slots: ? ??void?newConnect(); //連接服務器 ? ??void?readMessage();??//接收數據 void displayError(QAbstractSocket::SocketError);??//顯示錯誤

4.widget.cpp文件中做更改。
1)在構造函數中添加代碼: tcpSocket = new QTcpSocket(this); connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readMessage())); connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)), ? ?? ?? ?this,SLOT(displayError(QAbstractSocket::SocketError))); 這里關聯了tcpSocket的兩個信號,當有數據到來時發出readyRead()信號,我們執行讀取數據的readMessage()函數。當出現錯誤時發出error()信號,我們執行displayError()槽函數。
(2)實現newConnect()函數。 void?Widget::newConnect() { ? ??blockSize = 0; //初始化其為0 tcpSocket->abort(); //取消已有的連接
//連接到主機,這里從界面獲取主機地址和端口號 ? ??tcpSocket->connectToHost(ui->hostLineEdit->text(), ? ?? ?? ?? ?? ?? ?? ?? ?? ???ui->portLineEdit->text().toInt()); } 這個函數實現了連接到服務器,下面會在“連接”按鈕的單擊事件槽函數中調用這個函數。
(3)實現readMessage()函數。 void?Widget::readMessage() { ? ??QDataStream?in(tcpSocket); ? ??in.setVersion(QDataStream::Qt_4_6); ? ??//設置數據流版本,這里要和服務器端相同 ? ??if(blockSize==0) //如果是剛開始接收數據 ? ??{ ? ?? ??//判斷接收的數據是否有兩字節,也就是文件的大小信息 ? ?? ??//如果有則保存到blockSize變量中,沒有則返回,繼續接收數據 ? ?? ??if(tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return; ? ?? ??in >> blockSize; ? ??} ? ??if(tcpSocket->bytesAvailable() < blockSize) return; ? ??//如果沒有得到全部的數據,則返回,繼續接收數據 ? ??in >> message; ? ??//將接收到的數據存放到變量中 ? ??ui->messageLabel->setText(message); ? ??//顯示接收到的數據 } 這個函數實現了數據的接收,它與服務器端的發送函數相對應。首先我們要獲取文件的大小信息,然后根據文件的大小來判斷是否接收到了完整的文件。
(4)實現displayError()函數。 void?Widget::displayError(QAbstractSocket::SocketError) { ? ??qDebug() << tcpSocket->errorString(); //輸出錯誤信息 } 這里簡單的實現了錯誤信息的輸出。
(5)我們在widget.ui中進入“連接”按鈕的單擊事件槽函數,然后更改如下。 void?Widget::on_pushButton_clicked() //連接按鈕 { ? ??newConnect(); //請求連接 } 這里直接調用了newConnect()函數。

5.我們運行程序,同時運行服務器程序,然后在“主機”后填入“localhost”,在“端口號”后填入“6666,點擊“連接”按鈕,效果如下。

可以看到我們正確地接收到了數據。因為服務器端和客戶端是在同一臺機子上運行的,所以我這里填寫了“主機”為“localhost”,如果你在不同的機子上運行,需要在“主機”后填寫其正確的IP地址。


結語
到這里我們最簡單的TCP應用程序就完成了,在下一節我們將會對它進行擴展,實現任意文件的傳輸。


涉及到的源碼:??tcpServer.rar?(2.14 KB, 下載次數: 6)??tcpClient.rar?(2.54 KB, 下載次數: 6)?



與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

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

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