C++?Qt開發(fā)之使用QUdpSocket實(shí)現(xiàn)UDP網(wǎng)絡(luò)通信
Qt 是一個(gè)跨平臺(tái)C++圖形界面開發(fā)庫,利用Qt可以快速開發(fā)跨平臺(tái)窗體應(yīng)用程序,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實(shí)現(xiàn)圖形化開發(fā)極大的方便了開發(fā)效率,本章將重點(diǎn)介紹如何運(yùn)用QUdpSocket組件實(shí)現(xiàn)基于UDP的網(wǎng)絡(luò)通信功能。
與QTcpSocket組件功能類似,QUdpSocket組件是 Qt 中用于實(shí)現(xiàn)用戶數(shù)據(jù)報(bào)協(xié)議(UDP,User Datagram Protocol)通信的類。UDP 是一種無連接的、不可靠的數(shù)據(jù)傳輸協(xié)議,它不保證數(shù)據(jù)包的順序和可靠性,但具有低延遲和簡(jiǎn)單的特點(diǎn)。
以下是 QUdpSocket 類的完整函數(shù)及其簡(jiǎn)要解釋:
| 函數(shù) | 描述 |
|---|---|
| QUdpSocket(QObject *parent = nullptr) | 構(gòu)造函數(shù),創(chuàng)建一個(gè)新的 QUdpSocket 對(duì)象。 |
| ~QUdpSocket() | 析構(gòu)函數(shù),釋放 QUdpSocket 對(duì)象及其資源。 |
| void bind(const QHostAddress &address, quint16 port, BindMode mode = DefaultForPlatform) | 將套接字綁定到指定的本地地址和端口。 |
| void close() | 關(guān)閉套接字。 |
| bool joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface = QNetworkInterface()) | 加入多播組。 |
| bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface = QNetworkInterface()) | 離開多播組。 |
| qint64 pendingDatagramSize() const | 返回下一個(gè)待讀取的數(shù)據(jù)報(bào)的大小。 |
| qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 讀取數(shù)據(jù)報(bào)。 |
| QByteArray readDatagram(qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 讀取數(shù)據(jù)報(bào),返回 QByteArray 對(duì)象。 |
| qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port) | 發(fā)送數(shù)據(jù)報(bào)。 |
| qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &address, quint16 port) | 發(fā)送數(shù)據(jù)報(bào),接受 QByteArray 對(duì)象。 |
| QAbstractSocket::SocketState state() const | 返回套接字的當(dāng)前狀態(tài)。 |
| QAbstractSocket::SocketType socketType() const | 返回套接字的類型。 |
| bool isValid() const | 如果套接字有效,則返回 true;否則返回 false。 |
| int error() const | 返回套接字的當(dāng)前錯(cuò)誤代碼。 |
| QHostAddress localAddress() const | 返回本地地址。 |
| quint16 localPort() const | 返回本地端口。 |
| int readBufferSize() const | 返回讀取緩沖區(qū)的大小。 |
| void setReadBufferSize(int size) | 設(shè)置讀取緩沖區(qū)的大小。 |
| QNetworkInterface multicastInterface() const | 返回多播組的網(wǎng)絡(luò)接口。 |
| void setMulticastInterface(const QNetworkInterface &iface) | 設(shè)置多播組的網(wǎng)絡(luò)接口。 |
| bool hasPendingDatagrams() const | 如果有待讀取的數(shù)據(jù)報(bào),則返回 true;否則返回 false。 |
| bool isReadable() const | 如果套接字可讀,則返回 true;否則返回 false。 |
| bool isWritable() const | 如果套接字可寫,則返回 true;否則返回 false。 |
| bool setSocketDescriptor(int socketDescriptor, QUdpSocket::SocketState socketState = ConnectedState, QIODevice::OpenMode openMode = ReadWrite) | 設(shè)置套接字描述符。 |
| int socketDescriptor() const | 返回套接字描述符。 |
| bool waitForReadyRead(int msecs = 30000) | 等待套接字可讀取數(shù)據(jù)。 |
| bool waitForBytesWritten(int msecs = 30000) | 等待套接字已寫入指定字節(jié)數(shù)的數(shù)據(jù)。 |
| void ignoreSslErrors(const QList<QSslError> &errors) | 忽略 SSL 錯(cuò)誤。 |
| void abort() | 強(qiáng)制關(guān)閉套接字。 |
| QNetworkProxy proxy() const | 返回套接字的代理設(shè)置。 |
| void setProxy(const QNetworkProxy &networkProxy) | 設(shè)置套接字的代理設(shè)置。 |
| QString errorString() const | 返回套接字的錯(cuò)誤消息字符串。 |
這些函數(shù)提供了在 UDP 通信中使用 QUdpSocket 的各種功能,包括綁定、發(fā)送和接收數(shù)據(jù)報(bào)、設(shè)置和獲取套接字的狀態(tài)等。
1.初始化部分
在初始化部分我們首先通過new QUdpSocket來實(shí)現(xiàn)創(chuàng)建UDP對(duì)象,QUdpSocket 構(gòu)造函數(shù)的函數(shù)原型如下:
QUdpSocket::QUdpSocket(QObject * parent = nullptr)
如上構(gòu)造函數(shù)創(chuàng)建一個(gè)新的 QUdpSocket 對(duì)象。如果提供了 parent 參數(shù),則會(huì)將新創(chuàng)建的 QUdpSocket 對(duì)象添加到 parent 對(duì)象的子對(duì)象列表中,并且在 parent 對(duì)象被銷毀時(shí)自動(dòng)銷毀 QUdpSocket 對(duì)象。如果沒有提供 parent 參數(shù),則 QUdpSocket 對(duì)象將不會(huì)有父對(duì)象,并且需要手動(dòng)管理其生命周期。
初始化結(jié)束后,則下一步需要調(diào)用bind(),bind() 函數(shù)是 QUdpSocket 類的一個(gè)成員函數(shù),用于將套接字綁定到特定的本地地址和端口。它的函數(shù)原型如下:
void QUdpSocket::bind(const QHostAddress &address, quint16 port, BindMode mode = DefaultForPlatform)
address:要綁定的本地地址,通常是QHostAddress::Any,表示綁定到所有可用的網(wǎng)絡(luò)接口。port:要綁定的本地端口號(hào)。mode:綁定模式,指定套接字的行為。默認(rèn)值是DefaultForPlatform,表示使用平臺(tái)默認(rèn)的綁定模式。
該函數(shù)允許 QUdpSocket 在本地網(wǎng)絡(luò)接口上監(jiān)聽傳入的數(shù)據(jù)報(bào)。一旦調(diào)用了 bind() 函數(shù),QUdpSocket 就可以接收來自指定地址和端口的數(shù)據(jù)報(bào)。
在調(diào)用 bind() 函數(shù)之后,如果成功綁定了指定的地址和端口,套接字將處于 BoundState 狀態(tài)。如果出現(xiàn)錯(cuò)誤,可以通過檢查 error() 函數(shù)獲取錯(cuò)誤代碼,并通過 errorString() 函數(shù)獲取錯(cuò)誤消息。
接著我們通過connect()函數(shù)依次綁定套接字到stateChanged狀態(tài)改變信號(hào),以及readyRead()讀取信號(hào)上,這段初始化代碼如下所示;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
udpSocket=new QUdpSocket(this);
// 生成隨機(jī)整數(shù) 包含2000 - 不包含65534
int randomInt = QRandomGenerator::global()->bounded(2000, 65534);
if(udpSocket->bind(randomInt))
{
this->setWindowTitle(this->windowTitle() + " | 地址: " + getLocalAddress() + " 綁定端口:" + QString::number(udpSocket->localPort()));
}
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(udpSocket->state());
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}
接著切換到讀取信號(hào)所對(duì)應(yīng)的槽函數(shù)上,onSocketReadyRead是我們自定義的一個(gè)槽,該槽函數(shù)功能如下所示;
// 讀取收到的數(shù)據(jù)報(bào)
void MainWindow::onSocketReadyRead()
{
while(udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
QString str=datagram.data();
QString peer="[消息來自 " + peerAddr.toString()+":"+QString::number(peerPort)+"] | ";
ui->plainTextEdit->appendPlainText(peer+str);
}
}
首先在代碼中調(diào)用pendingDatagramSize函數(shù),pendingDatagramSize() 是 QUdpSocket 類的一個(gè)成員函數(shù),用于獲取下一個(gè)待讀取的數(shù)據(jù)報(bào)的大小。它的函數(shù)原型如下:
qint64 QUdpSocket::pendingDatagramSize() const
該函數(shù)返回一個(gè) qint64 類型的值,表示下一個(gè)待讀取的數(shù)據(jù)報(bào)的大?。ㄒ宰止?jié)為單位)。如果沒有待讀取的數(shù)據(jù)報(bào),或者發(fā)生了錯(cuò)誤,該函數(shù)將返回 -1。
通常,可以在調(diào)用 readDatagram() 函數(shù)之前調(diào)用 pendingDatagramSize() 函數(shù)來獲取下一個(gè)待讀取的數(shù)據(jù)報(bào)的大小。這樣可以為數(shù)據(jù)緩沖區(qū)分配正確大小的空間,以確保完整地讀取數(shù)據(jù)報(bào)。
當(dāng)有了待讀取字節(jié)后,接著就可以直接通過調(diào)用readDatagram函數(shù)來從套接字中讀取數(shù)據(jù)報(bào),readDatagram() 是 QUdpSocket 類的一個(gè)成員函數(shù),它有幾個(gè)重載形式,其中最常用的是:
qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize, QHostAddress * address = nullptr, quint16 * port = nullptr)
該函數(shù)用于讀取數(shù)據(jù)報(bào)并將其存儲(chǔ)到指定的緩沖區(qū) data 中,最多讀取 maxSize 個(gè)字節(jié)的數(shù)據(jù)??蛇x參數(shù) address 和 port 用于返回?cái)?shù)據(jù)報(bào)的源地址和端口號(hào)。如果不需要這些信息,可以將它們?cè)O(shè)置為 nullptr。
函數(shù)返回實(shí)際讀取的字節(jié)數(shù),如果發(fā)生錯(cuò)誤,返回 -1。要查看錯(cuò)誤信息,可以使用 error() 和 errorString() 函數(shù)。
另外,還有一個(gè)更簡(jiǎn)單的重載形式:
QByteArray QUdpSocket::readDatagram(qint64 maxSize, QHostAddress * address = nullptr, quint16 * port = nullptr)
這個(gè)重載函數(shù)直接返回一個(gè) QByteArray 對(duì)象,其中包含了讀取的數(shù)據(jù)報(bào)。
2.單播與廣播消息
單播(Unicast)和廣播(Broadcast)是網(wǎng)絡(luò)通信中常見的兩種數(shù)據(jù)傳輸方式,它們?cè)跀?shù)據(jù)包的傳輸范圍和目標(biāo)數(shù)量上有所不同。
單播(Unicast)
單播是一種一對(duì)一的通信方式,其中數(shù)據(jù)包從一個(gè)發(fā)送者傳輸?shù)揭粋€(gè)接收者。在單播通信中,數(shù)據(jù)包只發(fā)送到目標(biāo)主機(jī)的網(wǎng)絡(luò)接口,并且只有目標(biāo)主機(jī)能夠接收和處理這個(gè)數(shù)據(jù)包。
- 一對(duì)一通信:每個(gè)數(shù)據(jù)包只有一個(gè)發(fā)送者和一個(gè)接收者。
- 目標(biāo)明確:數(shù)據(jù)包只發(fā)送到特定的目標(biāo)主機(jī),其他主機(jī)不會(huì)接收到這個(gè)數(shù)據(jù)包。
- 點(diǎn)到點(diǎn)通信:適用于直接通信的場(chǎng)景,如客戶端與服務(wù)器之間的通信。
當(dāng)按鈕發(fā)送消息被點(diǎn)擊后,則是一種單播模式,通常該模式需要得到目標(biāo)地址與端口號(hào),并通過調(diào)用writeDatagram來實(shí)現(xiàn)數(shù)據(jù)的發(fā)送,該函數(shù)通過傳入三個(gè)參數(shù),分別是發(fā)送字符串,目標(biāo)地址與目標(biāo)端口來實(shí)現(xiàn)一對(duì)一推送。
void MainWindow::on_pushButton_clicked()
{
QHostAddress targetAddr(ui->lineEdit_addr->text());
QString portString = ui->lineEdit_port->text();
quint16 targetPort = portString.toUShort();
QString msg=ui->lineEdit_msg->text();
QByteArray str=msg.toUtf8();
// 發(fā)送數(shù)據(jù)報(bào)
udpSocket->writeDatagram(str,targetAddr,targetPort);
ui->plainTextEdit->appendPlainText("[單播消息] | " + msg);
}
廣播(Broadcast)
廣播是一種一對(duì)多的通信方式,其中數(shù)據(jù)包從一個(gè)發(fā)送者傳輸?shù)酵痪W(wǎng)絡(luò)中的所有主機(jī)。在廣播通信中,數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)中的所有主機(jī),并且所有的主機(jī)都能夠接收和處理這個(gè)數(shù)據(jù)包。
- 一對(duì)多通信:每個(gè)數(shù)據(jù)包有一個(gè)發(fā)送者,但可以有多個(gè)接收者。
- 目標(biāo)不明確:數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)中的所有主機(jī),不需要知道接收者的具體地址。
- 廣播域:在局域網(wǎng)中進(jìn)行廣播,只有在同一廣播域內(nèi)的主機(jī)才能接收到廣播消息。
- 網(wǎng)絡(luò)負(fù)載:在大型網(wǎng)絡(luò)中使用廣播可能會(huì)產(chǎn)生大量的網(wǎng)絡(luò)流量,影響網(wǎng)絡(luò)性能。
當(dāng)按鈕廣播消息被點(diǎn)擊后,則同樣是調(diào)用writeDatagram函數(shù)與,唯一的區(qū)別在于第二個(gè)參數(shù)并未指定地址,而是使用了QHostAddress::Broadcast來代替,意味著只要端口是一致的則對(duì)所有的客戶推送消息,其他保持不變。
void MainWindow::on_pushButton_2_clicked()
{
// 廣播地址
QString portString = ui->lineEdit_port->text();
quint16 targetPort = portString.toUShort();
QString msg=ui->lineEdit_msg->text();
QByteArray str=msg.toUtf8();
udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);
ui->plainTextEdit->appendPlainText("[廣播消息] | " + msg);
}
讀者可自行運(yùn)行兩次客戶端,此時(shí)的端口將會(huì)隨機(jī)分配,當(dāng)指定對(duì)端端口后就可以向其發(fā)送數(shù)據(jù),如下圖所示;具體實(shí)現(xiàn)細(xì)節(jié),請(qǐng)參考文章附件。

以上就是C++ Qt開發(fā)之使用QUdpSocket實(shí)現(xiàn)UDP網(wǎng)絡(luò)通信的詳細(xì)內(nèi)容,更多關(guān)于Qt QUdpSocket UDP網(wǎng)絡(luò)通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言實(shí)現(xiàn)掃雷小游戲(擴(kuò)展版)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)擴(kuò)展版的掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
C語言基礎(chǔ)知識(shí)點(diǎn)解析(extern,static,typedef,const)
本篇文章是對(duì)C語言基礎(chǔ)知識(shí)點(diǎn)(extern,static,typedef,const)的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-10-10
C++實(shí)現(xiàn)圖書管理系統(tǒng)源碼
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)圖書管理系統(tǒng)源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
C++中的四個(gè)默認(rèn)成員函數(shù)與運(yùn)算符重載詳解
這篇文章主要給大家介紹了關(guān)于C++中四個(gè)默認(rèn)成員函數(shù)與運(yùn)算符重載的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08
C++實(shí)現(xiàn)的分布式游戲服務(wù)端引擎KBEngine詳解
這篇文章主要詳細(xì)介紹了C++實(shí)現(xiàn)的分布式游戲服務(wù)端引擎KBEngine的概念以及使用方法,非常的實(shí)用,有需要的小伙伴可以參考下2015-03-03

