当前位置:   article > 正文

Qt实现UDP单播和组播功能_qt udp组播

qt udp组播

一、UDP单播、广播和组播的说明

UDP是不可靠、无连接的,所以划分为发送方和接收方更好理解 

1、单播

UDP是无连接的,进行单播通信时,必须要绑定接收方端口,发送方直接通过接收方的ip和绑定的端口进行通信。发送方可以绑定端口也可以不用绑定端口,不绑定端口的话,系统会随机分配端口。

对于多网卡来说,需要指定网卡,绑定一个网卡的ip。如果不进行显式的绑定操作QUdpSocket 对象将会使用默认的绑定方式,自动选择一个可用的 ip 地址进行绑定。这种情况下,数据包可能通过任意一个可用的网络接口发送出去。

2、广播

对于只有1个网卡的主机来说,可以不用显示绑定ip,发送方直接发送广播就行,接收方绑定广播端口就行,这样才能看到收到的消息。 

对于多网卡来说,要指定唯一的网卡并且在广播前要绑定广播端口,ip可以不用绑定,系统分配任意一个ip

3、组播 

对于只有1个网卡的主机来说,可以不用绑定ip,直接绑定端口后加入组播就行。系统分配任意一个ip ,相当于 QHostAddress::AnyIPv4。

对于多网卡来说,要指定唯一的网卡并且在加入组播前要绑定组播端口,ip可以不用绑定,系统分配任意一个ip

注意:指定网卡不等于指定ip!!! 

1、使用 setMulticastInterface 方法可以指定一个明确的网卡,但并不意味着只有一个 IP 地址。一个网卡可以绑定多个 IP 地址,例如在同一台主机上同时存在有线网卡和无线网卡,它们可能都连接到同一局域网,并分别配置了不同的 IP 地址。此时,通过 setMulticastInterface 方法指定了一个明确的网卡后,并不确定使用哪个 IP 地址来进行组播通信。

如果需要确保使用特定的 IP 地址进行组播通信,则需要使用 bind 方法来将 QUdpSocket 对象绑定到具体的 IP 地址和端口上,这样每次进行组播通信时,都会使用该 IP 地址来发送和接收数据报文。 

2、使用 QHostAddress::AnyIPv4 参数可以将 QUdpSocket 对象绑定到本机的所有 IPv4 地址。这意味着,该 QUdpSocket 对象可以接收通过本机的任意一个 IPv4 地址发送到指定端口的数据包

然而,需要注意的是,绑定到多个 IPv4 地址并不意味着可以同时从多个地址接收数据包。在任何给定的时刻,QUdpSocket 对象只能通过一个 IP 地址接收数据包。

当有多个 IPv4 地址可用时,QUdpSocket 对象会选择其中一个地址来接收数据包。这个选择通常由操作系统或网络栈决定,并且可能会受到各种因素的影响,例如网络接口的优先级、路由表等。

因此,使用 QHostAddress::AnyIPv4 参数可以让 QUdpSocket 对象绑定到本机的所有 IPv4 地址,但实际上它只能通过其中一个地址接收数据包。具体使用哪个地址取决于操作系统和网络环境。

二、遇到的UDP通信的问题参考

      关于QT UDP组播的几个问题icon-default.png?t=N7T8https://blog.csdn.net/tom06/article/details/52163665?spm=1001.2014.3001.5506

UDP多播/组播通信,同一局域网下的两台机器通信接收不到数据icon-default.png?t=N7T8https://blog.csdn.net/qq_43290013/article/details/117288296?spm=1001.2014.3001.5506

QT读取网卡列表多网卡绑定组播网卡icon-default.png?t=N7T8https://blog.csdn.net/qq_30727593/article/details/127441711?spm=1001.2014.3001.5506

三、效果与代码

1、在.pro文件中添加如下内容:

QT       += network

2、在.h文件中添加串口所用的头文件

  1. #include <QUdpSocket>
  2. #include <QNetworkInterface>

 3、添加一个QUdpSocket* socket的类成员,并在.cpp中实例化对象:

socket = new QUdpSocket;

 4、扫描可用网口

  1. QList<QNetworkInterface> interfaceList = QNetworkInterface::allInterfaces();
  2. foreach (QNetworkInterface nif, interfaceList) {
  3. // 检查网卡是否有效并已经启用
  4. if (nif.isValid() && nif.flags().testFlag(QNetworkInterface::IsUp)) {
  5. // 将已经启用的网卡名称添加到列表中
  6. enabledInterfaceList.append(nif);
  7. QList<QNetworkAddressEntry> entries = nif.addressEntries();
  8. foreach (QNetworkAddressEntry entry, entries) {
  9. if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
  10. ui->nif_config->addItem(entry.ip().toString());
  11. }
  12. }
  13. }
  14. }

5、对组播进行设置(可以忽略)

  1. //组播的数据的生存期,数据报没跨1个路由就会减1.表示多播数据报只能在同一路由下的局域网内传播
  2. socket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
  3. //1是允许loopback模式(自发自收),0是阻止。
  4. socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, true);

6、初始化组播设置

  1. int MainWindow::init_group(QUdpSocket *socket, QNetworkInterface &currentInterface, QHostAddress &localAddress, QHostAddress &targetAddress, int localPort)
  2. {
  3. if (targetAddress.isMulticast()) {//isMulticast()判断是否是组播地址
  4. if (localPort == 0) {//判断是否指定本地端口
  5. //bind(localAddress, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)可以换成bind(QHostAddress::AnyIPv4, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)
  6. if (socket->bind(localAddress, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) {
  7. socket->setMulticastInterface(currentInterface);
  8. if (socket->joinMulticastGroup(targetAddress, currentInterface)) {
  9. qDebug() << "未指定本地端口,加入组播成功";
  10. return 1;
  11. } else {
  12. qDebug() << "未指定本地端口,加入组播失败";
  13. return -1;
  14. }
  15. } else {
  16. qDebug() << "未指定本地端口,绑定失败";
  17. return -1;
  18. }
  19. } else {
  20. if (socket->bind(localAddress, localPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) {
  21. socket->setMulticastInterface(currentInterface);
  22. if (socket->joinMulticastGroup(targetAddress, currentInterface)) {
  23. qDebug() << "指定本地端口,加入组播成功";
  24. return 1;
  25. } else {
  26. qDebug() << "指定本地端口,加入组播失败";
  27. return -1;
  28. }
  29. } else {
  30. qDebug() << "指定本地端口,绑定失败";
  31. return -1;
  32. }
  33. }
  34. } else {
  35. qDebug() << "目标地址不是组播地址";
  36. return -1;
  37. }
  38. // 如果执行到这里,说明没有通过任何返回语句,应该是一个错误
  39. qDebug() << "init_group 函数执行路径错误";
  40. return -1; // 或者抛出异常,取决于您希望如何处理这种情况
  41. }

7、发送消息

socket->writeDatagram(str,targetAddress,targetPort);

8、接收消息

  1. //接收消息
  2. connect(udpSocket,&QUdpSocket::readyRead,this,[&](){
  3. QByteArray datagram;
  4. datagram.resize(udpSocket->pendingDatagramSize());
  5. udpSocket->readDatagram(datagram.data(), datagram.size());
  6. ui->recvTextEdit->append(datagram);
  7. });

9、退出组播

  1. int MainWindow::exit_group(QUdpSocket *socket ,QNetworkInterface &currentInterface, QHostAddress &targetAddress)
  2. {
  3. if(socket->leaveMulticastGroup(targetAddress,currentInterface)){
  4. socket->abort();
  5. qDebug() << "退出组播成功";
  6. return 1;
  7. }else{
  8. qDebug() << "退出组播失败";
  9. return -1;
  10. }
  11. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/627135
推荐阅读
相关标签
  

闽ICP备14008679号