C++?Qt開發(fā)之使用QUdpSocket實現(xiàn)UDP網(wǎng)絡(luò)通信
Qt 是一個跨平臺C++圖形界面開發(fā)庫,利用Qt可以快速開發(fā)跨平臺窗體應(yīng)用程序,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現(xiàn)圖形化開發(fā)極大的方便了開發(fā)效率,本章將重點介紹如何運用QUdpSocket
組件實現(xiàn)基于UDP的網(wǎng)絡(luò)通信功能。
與QTcpSocket
組件功能類似,QUdpSocket
組件是 Qt 中用于實現(xiàn)用戶數(shù)據(jù)報協(xié)議(UDP,User Datagram Protocol)通信的類。UDP 是一種無連接的、不可靠的數(shù)據(jù)傳輸協(xié)議,它不保證數(shù)據(jù)包的順序和可靠性,但具有低延遲和簡單的特點。
以下是 QUdpSocket
類的完整函數(shù)及其簡要解釋:
函數(shù) | 描述 |
---|---|
QUdpSocket(QObject *parent = nullptr) | 構(gòu)造函數(shù),創(chuàng)建一個新的 QUdpSocket 對象。 |
~QUdpSocket() | 析構(gòu)函數(shù),釋放 QUdpSocket 對象及其資源。 |
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 | 返回下一個待讀取的數(shù)據(jù)報的大小。 |
qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 讀取數(shù)據(jù)報。 |
QByteArray readDatagram(qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 讀取數(shù)據(jù)報,返回 QByteArray 對象。 |
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port) | 發(fā)送數(shù)據(jù)報。 |
qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &address, quint16 port) | 發(fā)送數(shù)據(jù)報,接受 QByteArray 對象。 |
QAbstractSocket::SocketState state() const | 返回套接字的當前狀態(tài)。 |
QAbstractSocket::SocketType socketType() const | 返回套接字的類型。 |
bool isValid() const | 如果套接字有效,則返回 true;否則返回 false。 |
int error() const | 返回套接字的當前錯誤代碼。 |
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ù)報,則返回 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 錯誤。 |
void abort() | 強制關(guān)閉套接字。 |
QNetworkProxy proxy() const | 返回套接字的代理設(shè)置。 |
void setProxy(const QNetworkProxy &networkProxy) | 設(shè)置套接字的代理設(shè)置。 |
QString errorString() const | 返回套接字的錯誤消息字符串。 |
這些函數(shù)提供了在 UDP 通信中使用 QUdpSocket
的各種功能,包括綁定、發(fā)送和接收數(shù)據(jù)報、設(shè)置和獲取套接字的狀態(tài)等。
1.初始化部分
在初始化部分我們首先通過new QUdpSocket
來實現(xiàn)創(chuàng)建UDP對象,QUdpSocket
構(gòu)造函數(shù)的函數(shù)原型如下:
QUdpSocket::QUdpSocket(QObject * parent = nullptr)
如上構(gòu)造函數(shù)創(chuàng)建一個新的 QUdpSocket
對象。如果提供了 parent
參數(shù),則會將新創(chuàng)建的 QUdpSocket
對象添加到 parent
對象的子對象列表中,并且在 parent
對象被銷毀時自動銷毀 QUdpSocket
對象。如果沒有提供 parent
參數(shù),則 QUdpSocket
對象將不會有父對象,并且需要手動管理其生命周期。
初始化結(jié)束后,則下一步需要調(diào)用bind()
,bind()
函數(shù)是 QUdpSocket
類的一個成員函數(shù),用于將套接字綁定到特定的本地地址和端口。它的函數(shù)原型如下:
void QUdpSocket::bind(const QHostAddress &address, quint16 port, BindMode mode = DefaultForPlatform)
address
:要綁定的本地地址,通常是QHostAddress::Any
,表示綁定到所有可用的網(wǎng)絡(luò)接口。port
:要綁定的本地端口號。mode
:綁定模式,指定套接字的行為。默認值是DefaultForPlatform
,表示使用平臺默認的綁定模式。
該函數(shù)允許 QUdpSocket
在本地網(wǎng)絡(luò)接口上監(jiān)聽傳入的數(shù)據(jù)報。一旦調(diào)用了 bind()
函數(shù),QUdpSocket
就可以接收來自指定地址和端口的數(shù)據(jù)報。
在調(diào)用 bind()
函數(shù)之后,如果成功綁定了指定的地址和端口,套接字將處于 BoundState
狀態(tài)。如果出現(xiàn)錯誤,可以通過檢查 error()
函數(shù)獲取錯誤代碼,并通過 errorString()
函數(shù)獲取錯誤消息。
接著我們通過connect()
函數(shù)依次綁定套接字到stateChanged
狀態(tài)改變信號,以及readyRead()
讀取信號上,這段初始化代碼如下所示;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); udpSocket=new QUdpSocket(this); // 生成隨機整數(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())); }
接著切換到讀取信號所對應(yīng)的槽函數(shù)上,onSocketReadyRead
是我們自定義的一個槽,該槽函數(shù)功能如下所示;
// 讀取收到的數(shù)據(jù)報 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
類的一個成員函數(shù),用于獲取下一個待讀取的數(shù)據(jù)報的大小。它的函數(shù)原型如下:
qint64 QUdpSocket::pendingDatagramSize() const
該函數(shù)返回一個 qint64
類型的值,表示下一個待讀取的數(shù)據(jù)報的大?。ㄒ宰止?jié)為單位)。如果沒有待讀取的數(shù)據(jù)報,或者發(fā)生了錯誤,該函數(shù)將返回 -1。
通常,可以在調(diào)用 readDatagram()
函數(shù)之前調(diào)用 pendingDatagramSize()
函數(shù)來獲取下一個待讀取的數(shù)據(jù)報的大小。這樣可以為數(shù)據(jù)緩沖區(qū)分配正確大小的空間,以確保完整地讀取數(shù)據(jù)報。
當有了待讀取字節(jié)后,接著就可以直接通過調(diào)用readDatagram
函數(shù)來從套接字中讀取數(shù)據(jù)報,readDatagram()
是 QUdpSocket
類的一個成員函數(shù),它有幾個重載形式,其中最常用的是:
qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize, QHostAddress * address = nullptr, quint16 * port = nullptr)
該函數(shù)用于讀取數(shù)據(jù)報并將其存儲到指定的緩沖區(qū) data
中,最多讀取 maxSize
個字節(jié)的數(shù)據(jù)??蛇x參數(shù) address
和 port
用于返回數(shù)據(jù)報的源地址和端口號。如果不需要這些信息,可以將它們設(shè)置為 nullptr
。
函數(shù)返回實際讀取的字節(jié)數(shù),如果發(fā)生錯誤,返回 -1。要查看錯誤信息,可以使用 error()
和 errorString()
函數(shù)。
另外,還有一個更簡單的重載形式:
QByteArray QUdpSocket::readDatagram(qint64 maxSize, QHostAddress * address = nullptr, quint16 * port = nullptr)
這個重載函數(shù)直接返回一個 QByteArray
對象,其中包含了讀取的數(shù)據(jù)報。
2.單播與廣播消息
單播(Unicast)和廣播(Broadcast)是網(wǎng)絡(luò)通信中常見的兩種數(shù)據(jù)傳輸方式,它們在數(shù)據(jù)包的傳輸范圍和目標數(shù)量上有所不同。
單播(Unicast)
單播是一種一對一的通信方式,其中數(shù)據(jù)包從一個發(fā)送者傳輸?shù)揭粋€接收者。在單播通信中,數(shù)據(jù)包只發(fā)送到目標主機的網(wǎng)絡(luò)接口,并且只有目標主機能夠接收和處理這個數(shù)據(jù)包。
- 一對一通信:每個數(shù)據(jù)包只有一個發(fā)送者和一個接收者。
- 目標明確:數(shù)據(jù)包只發(fā)送到特定的目標主機,其他主機不會接收到這個數(shù)據(jù)包。
- 點到點通信:適用于直接通信的場景,如客戶端與服務(wù)器之間的通信。
當按鈕發(fā)送消息被點擊后,則是一種單播模式,通常該模式需要得到目標地址與端口號,并通過調(diào)用writeDatagram
來實現(xiàn)數(shù)據(jù)的發(fā)送,該函數(shù)通過傳入三個參數(shù),分別是發(fā)送字符串,目標地址與目標端口來實現(xiàn)一對一推送。
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ù)報 udpSocket->writeDatagram(str,targetAddr,targetPort); ui->plainTextEdit->appendPlainText("[單播消息] | " + msg); }
廣播(Broadcast)
廣播是一種一對多的通信方式,其中數(shù)據(jù)包從一個發(fā)送者傳輸?shù)酵痪W(wǎng)絡(luò)中的所有主機。在廣播通信中,數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)中的所有主機,并且所有的主機都能夠接收和處理這個數(shù)據(jù)包。
- 一對多通信:每個數(shù)據(jù)包有一個發(fā)送者,但可以有多個接收者。
- 目標不明確:數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)中的所有主機,不需要知道接收者的具體地址。
- 廣播域:在局域網(wǎng)中進行廣播,只有在同一廣播域內(nèi)的主機才能接收到廣播消息。
- 網(wǎng)絡(luò)負載:在大型網(wǎng)絡(luò)中使用廣播可能會產(chǎn)生大量的網(wǎng)絡(luò)流量,影響網(wǎng)絡(luò)性能。
當按鈕廣播消息被點擊后,則同樣是調(diào)用writeDatagram
函數(shù)與,唯一的區(qū)別在于第二個參數(shù)并未指定地址,而是使用了QHostAddress::Broadcast
來代替,意味著只要端口是一致的則對所有的客戶推送消息,其他保持不變。
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); }
讀者可自行運行兩次客戶端,此時的端口將會隨機分配,當指定對端端口后就可以向其發(fā)送數(shù)據(jù),如下圖所示;具體實現(xiàn)細節(jié),請參考文章附件。
以上就是C++ Qt開發(fā)之使用QUdpSocket實現(xiàn)UDP網(wǎng)絡(luò)通信的詳細內(nèi)容,更多關(guān)于Qt QUdpSocket UDP網(wǎng)絡(luò)通信的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++右值引用與move和forward函數(shù)的使用詳解
為了支持移動操作,新標準引入了一種新的引用類型——右值引用(rvalue reference)。所謂右值引用就是必須綁定到右值的引用,這篇文章主要介紹了C++右值引用與move和forward的使用2022-08-08C++輕量級界面開發(fā)框架ImGUI介紹小結(jié)
如果從事過C++?Windows客戶端開發(fā),大家對MFC、Qt、DuiLib等各種DirectUI應(yīng)該有了解,本篇給大家介紹一個超級輕量級的C++開源跨平臺圖形界面框架ImGUI,感興趣的可以了解一下2021-11-11C語言 詳細分析結(jié)構(gòu)體的內(nèi)存對齊
C 數(shù)組允許定義可存儲相同類型數(shù)據(jù)項的變量,結(jié)構(gòu)是 C 編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許你存儲不同類型的數(shù)據(jù)項,本篇讓我們來了解C 的結(jié)構(gòu)體內(nèi)存對齊2022-03-03