64.QT-單播、廣播、組播


本章主要描述QT中如何實現單播、廣播、組播,大家可以直接參考qt官方例子:

  • Broadcast Sender : 廣播方式發送
  • Broadcast Receiver : 廣播方式接收
  • Multicast Sender : 組播方式發送
  • Multicast Receive : 組播方式接收

需要用到的函數

bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform);
//使用BindMode模式綁定到端口端口上的地址。
//對於UDP套接字,綁定后,當UDP數據報到達指定的地址和端口時,信號QUdpSocket::readyRead()就會發出。因此,這個函數對於編寫UDP服務器很有用。
//對於TCP套接字,此函數可用於指定用於輸出連接的接口,這在多個網絡接口的情況下非常有用。
//默認情況下,套接字使用DefaultForPlatform BindMode綁定。如果不指定端口,則選擇隨機端口。
//如果成功,函數返回true,套接字進入BoundState;否則返回false。
mode取值有:
//QUdpSocket::ShareAddress : 允許其他server綁定到相同的地址和端口。當多個進程通過偵聽相同的地址和端口來共享單個server的負載時,這是很有用的。但是該選項需要考慮安全影響。
注意,通過將此選項與ReuseAddressHint結合,您還將允許您的服務重新綁定現有的共享地址。
//QUdpSocket::DontShareAddress: 綁定地址和端口,且不允許其他server進行綁定。可以保證在成功時,您的server是唯一偵聽地址和端口的服務。
QUdpSocket::ReuseAddressHint: 向QAbstractSocket提供一個提示,即即使地址和端口已經被另一個套接字綁定,它也應嘗試重新綁定server。
QUdpSocket::DefaultForPlatform: 平台的默認選項。在Unix和macOS上,它等價於(DontShareAddress + ReuseAddressHint),在Windows上,它等價於ShareAddress。

其中QHostAddress除了填指定地址外,還可以設置如下所示:
QHostAddress::Null - 空地址對象。相當於QHostAddress()。參見QHostAddress: isNull()
QHostAddress::LocalHost - IPv4本地主機地址。相當於QHostAddress(127.0.0.1)
QHostAddress::LocalHostIPv6 - IPv6本地主機地址。相當於QHostAddress("::1")
QHostAddress::Broadcast - IPv4廣播地址。相當於QHostAddress("255.255.255.255")
QHostAddress::AnyIPv4 - IPv4任何地址。相當於QHostAddress("0.0.0.0")。綁定此地址的套接字只能在IPv4接口上偵聽。
QHostAddress::AnyIPv6 - IPv6任何地址。相當於QHostAddress("::")。綁定此地址的套接字只能在IPv6接口上偵聽。
QHostAddress::Any - 任意地址。綁定此地址的套接字將同時監聽IPv4和IPv6接口。

bool QAbstractSocket::bind(quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform);
這是個重載函數,默認地址為QHostAddress:Any. 
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr);
//接收不大於maxSize字節的數據報,並將其存儲在數據中。發送者的主機地址和端口存儲在*address和*port中

qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port);
將大小為size的數據報發送到端口端口的主機地址地址。返回成功時發送的字節數;否則返回1. 由於udp不穩定.所以數據報數據量盡量少,通常不建議發送大於512字節的數據報. 如果在連接的UDP套接字上調用此函數可能導致錯誤,沒有數據包被發送。如果您正在使用已連接的套接字,請使用write()發送數據報。


QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize = -1)
//接收不大於maxSize字節的數據報,並將接受的數據報,以及發送者的主機地址和端口放在QNetworkDatagram對象中返回。

1.單播

單播用來一個UDP客戶端發出的數據報只發送到另一個指定地址和端口的UDP客戶端,是一對一的數據傳輸。
我們在以本地IP為例,初始化如下所示:

qDebug()<<"udpSocket1綁定: "<<udpSocket1->bind(QHostAddress::LocalHost, 7755); // 客戶端1
qDebug()<<"udpSocket1綁定: "<<udpSocket2->bind(QHostAddress::LocalHost, 7756); // 客戶端2

connect(udpSocket1, &QUdpSocket::readyRead,
this, &Widget::readPendingDatagrams);
connect(udpSocket2, &QUdpSocket::readyRead,
this, &Widget::readPendingDatagrams);

讀數據槽函數如下所示:

void Widget::readPendingDatagrams()
{
    QUdpSocket *udpSocket = dynamic_cast<QUdpSocket *>(sender());
    
    
    while (udpSocket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = udpSocket->receiveDatagram();
        qDebug()<<QString(datagram.data())<<","<<datagram.senderAddress()<<datagram.senderPort();
    }
}

然后添加兩個按鈕:

void Widget::on_pushButton_clicked()
{
    QString str = "1發送了數據";
    QByteArray datagram = str.toUtf8().data();
    udpSocket1->writeDatagram(datagram.data(),datagram.length(),QHostAddress::LocalHost,7756); // 發送給客戶端2綁定的端口號(如果未綁定就會發送失敗)
}

void Widget::on_pushButton_2_clicked()
{
    QString str = "2發送了數據";
    QByteArray datagram = str.toUtf8().data();
    udpSocket2->writeDatagram(datagram.data(),datagram.length(),QHostAddress::LocalHost,7755); // 發送給客戶端1綁定的端口號(如果未綁定就會發送失敗)
}

提示: 不管客戶端是否bind()成功與否,都可以調用writeDatagram()隨意往某個地址端口發送報文,因為UDP本身就是不需要建立連接的

如果我們想讓客戶端1和客戶端2都在同一個地址端口上收發消息,那么我們需要設置為:

qDebug()<<"udpSocket1綁定: "<<udpSocket1->bind(QHostAddress::LocalHost, 7755, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
qDebug()<<"udpSocket2綁定: "<<udpSocket2->bind(QHostAddress::LocalHost, 7755, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);

 

2.廣播
廣播指一個UDP客戶端發出的數據報,在同一網絡范圍內其他所有的UDP客戶端都可以收到。
廣播很簡單,我們以端口號45454為例:

  • 發送方調用udpSocket->writeDatagram(datagram, QHostAddress::Broadcast, 45454);即可實現廣播發送.
  • 接收方需要bind(45454, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)才行.等價於bind(QHostAddress::Any, 7755, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);

如果接收方只是bind自身地址(QHostAddress::LocalHost)是收不到消息的.

 

3.組播

組播也稱多播,凡是需要接受數據的客戶端都需要使用joinmultiastgroup()加入指定組播地址,然后發送方只要往指定組播地址發送數據。
加入指定組播地址的客戶端就會產生readyRead信號,然后調用readDatagram()從指定的組播地址和端口去取數據。

組播地址屬於D類ip,只支持239.0.0.0—239.255.255.255,需要用到的函數:

bool QUdpSocket:joinmultiastgroup(const QHostAddress &groupAddress);
//加入指定組播地址所在組,如果成功,這個函數返回true;否則它將返回false
bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress)
//離開指定組播地址所在組,如果成功,這個函數返回true;否則它將返回false

需要注意的是joinmultiastgroup()函數,如果我們加入的組播地址是IPv4,那么bind的也必須明確是IPv4地址,比如這樣就會加入失敗:

groupAddress = QHostAddress("239.255.43.21");
udpSocket1->bind(QHostAddress::Any, 7755, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
udpSocket1->joinMulticastGroup(groupAddress);

因為QHostAddress::Any包含了IPv6,而groupAddress是個IPv4地址.

組播示例,初始化如下所示:

udpSocket1 = new QUdpSocket(this);
udpSocket2 = new QUdpSocket(this);
udpSocket3 = new QUdpSocket(this);

groupAddress = QHostAddress("239.255.43.21");
udpSocket1->bind(QHostAddress::AnyIPv4, 7755, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
udpSocket2->bind(QHostAddress::AnyIPv4, 7755, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
udpSocket1->joinMulticastGroup(groupAddress);
udpSocket2->joinMulticastGroup(groupAddress);

connect(udpSocket1, &QUdpSocket::readyRead,
this, &Widget::readPendingDatagrams);
connect(udpSocket2, &QUdpSocket::readyRead,
this, &Widget::readPendingDatagrams);

然后實現下面函數:

void Widget::readPendingDatagrams()
{
    QUdpSocket *udpSocket = dynamic_cast<QUdpSocket *>(sender());
    
    
    while (udpSocket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = udpSocket->receiveDatagram();
        qDebug()<<QString(datagram.data())<<","<<datagram.senderAddress()<<datagram.senderPort();
    }
}


void Widget::on_pushButton_clicked()
{
    QString str = "udpSocket3往組播地址發送數據了";
    QByteArray datagram = str.toUtf8().data();
    udpSocket3->writeDatagram(datagram.data(),datagram.length(),groupAddress,7755);
}

void Widget::on_pushButton_2_clicked()
{
    QString str = "udpSocket1往組播地址發送數據了";
    QByteArray datagram = str.toUtf8().data();
    udpSocket1->writeDatagram(datagram.data(),datagram.length(),groupAddress,7755);
}

當我們點擊pushButton按鈕,就會讓udpSocket3往組播地址發送數據,此時udpSocket1和udpSocket2就會產生readyRead信號從而去組播地址獲取數據.
當我們點擊pushButton_2按鈕,就會讓udpSocket1往組播地址發送數據,此時udpSocket1和udpSocket2也會產生readyRead信號從而去組播地址獲取數據.


未完待續,下章學習: 65.QT-UDP組播實現多人共享桌面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM