QT组播的建立和使用(绑定特定的网卡,绑定特定IP)

https://blog.csdn.net/only_write_bug/article/details/159115776?spm=1011.2415.3001.5331

目录

一、组播介绍

二、组播初始化和使用

1、创建QUdpSocket对象

2、绑定本地端口

3、加入组播组

4、发送组播数据

5、接收组播数据

6、离开组播组

三、为什么需要指定网卡,如何绑定固定网卡

1、如何查看自己电脑有那些网卡信息

2、如何查找自己电脑对应IP的网卡信息以及绑定


一、组播介绍

组播(Multicast)是一种高效的网络通信方式,它允许将数据包同时发送给一组特定的接收者,而无需为每个接收者都复制一份数据。

组播地址 :IPv4组播地址范围是 224.0.0.0 ~ 239.255.255.255(D类地址)。其中 224.0.0.0 ~ 224.0.0.255 为本地链路组播地址(如 224.0.0.1 表示所有子网主机),通常不被应用程序使用;239.0.0.0 ~ 239.255.255.255 为管理权限组播地址(私有组播地址),适合内部网络使用。

组播组:加入同一个组播地址的所有主机形成一个组播组。发送者只需将数据发往组播地址,路由器会负责将数据复制到有接收者的链路上。

相关链接:

https://blog.csdn.net/only_write_bug/article/details/159115776?spm=1011.2415.3001.5331

二、组播初始化和使用

1、创建QUdpSocket对象

cpp 复制代码
QUdpSocket *udpSocket = new QUdpSocket(this);

2、绑定本地端口

// ShareAddress :允许共享地址。如前所述,允许多个套接字绑定到同一地址和端口

// ReuseAddressHint: 提示地址重用。为套接字提供一个"提示",告诉它即使地址和端口正被另一个套接字使用,也应尝试重新绑定。

// ShareAddress和ReuseAddressHint通常一起使用

这里绑定到 QHostAddress::AnyIPv4 表示监听所有网络接口上的指定端口。如果希望只监听特定网卡,可以传入该网卡的IP地址。

cpp 复制代码
udpSocket->bind(QHostAddress::AnyIPv4, 12345, QUdpSocket::ShareAddress);

这里为绑定到特定IP地址和端口,selfip为QString对象,所以需要转换为QHostAddress。selfip和port为你需要绑定的ip和端口。

cpp 复制代码
if (!udpSocket->bind(QHostAddress(selfip), myport, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) {
        qWarning() << "Failed to bind status socket:" << m_socket->errorString();
    }

3、加入组播组

cpp 复制代码
QHostAddress multicastAddr("239.255.43.21");
udpSocket->joinMulticastGroup(multicastAddr);

多个网卡的情况下,你已知自己需要绑定的网卡名称,可直接绑定到对应网卡。如下:

cpp 复制代码
QNetworkInterface interface = QNetworkInterface::interfaceFromName("eth0");
udpSocket->joinMulticastGroup(multicastAddr, interface);

4、发送组播数据

发送方不需要加入组播组,只需将数据报的目的地址设为组播地址即可。发送前建议设置组播数据包的TTL(生存时间):

cpp 复制代码
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1); // TTL=1 限制在本地子网
udpSocket->writeDatagram(datagram.data(), datagram.size(), multicastAddr, 12345);

5、接收组播数据

当有数据到达绑定的端口时,QUdpSocket 会发出 readyRead() 信号,在槽函数中读取:

cpp 复制代码
connect(udpSocket, &QUdpSocket::readyRead, this, [=](){
    while (udpSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;
        udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
        // 处理数据或者定义信号将数据传到其他线程进行处理...
    }
});

6、离开组播组

当你的程序不再需要接收组播信息时,应该先离开组播组

cpp 复制代码
udpSocket->leaveMulticastGroup(multicastAddr);

三、为什么需要指定网卡,如何绑定固定网卡

当你调用udpSocket->bind(QHostAddress::AnyIPv4, 12345, QUdpSocket::ShareAddress);绑定任意IP信息时,如果你的电脑只有一个网卡的话问题不大,但是如果有多个网卡就会出现接收不到信息的问题,因为电脑组播在接收数据流的时候可能会切换到其他网卡接收,导致组播接收数据出现丢失,但是不会影响你的组播信息发送。

所以在电脑有多网卡或者有虚拟环境的时候,推荐组播绑定到特定的网卡。

1、如何查看自己电脑有那些网卡信息

添加<QNetworkInterface> <QNetworkDatagram>头文件之后,可调用QT的 QNetworkInterface::allInterfaces()查看目前你的电脑可用的所有网卡信息。如果你已知你的网卡信息就不需要。

cpp 复制代码
QString info = "<<<<<<<<Available network interfaces>>>>>>>>";
    qDebug()<<info;
    foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) {
        if (iface.flags() & QNetworkInterface::IsUp &&
            iface.flags() & QNetworkInterface::IsRunning &&
            !(iface.flags() & QNetworkInterface::IsLoopBack)) {
            info = "  Interface:" + iface.name()
                   + "  Human readable:" + iface.humanReadableName();
            qDebug()<<info;
            foreach (QNetworkAddressEntry entry, iface.addressEntries()) {
                if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
                    info = "    IP:" + entry.ip().toString();
                    qDebug()<<info;
                }
            }
        }
    }

2、如何查找自己电脑对应IP的网卡信息以及绑定

self为自身IP信息。

cpp 复制代码
 QNetworkInterface   m_multicastIface;
if (!selfip.isEmpty()) 
{
        const auto interfaces = QNetworkInterface::allInterfaces();
        for (const QNetworkInterface &iface : interfaces) //遍历所有网卡信息
        {
            for (const QNetworkAddressEntry &entry : iface.addressEntries()) //遍历所有IP地址
            {
                if (entry.ip().toString() == selfip)//找到对应IP信息
                {
                    m_multicastIface = iface;
                    qDebug() << "找到匹配网卡:" << iface.humanReadableName() << "IP:" <<selfip;
                    break;
                }
            }
            if (m_multicastIface.isValid()) break;
        }
}

//加入组播组
if (m_multicastIface.isValid()) 
{
    if (m_socket->joinMulticastGroup(groupAddress, m_multicastIface)) 
    {
        qDebug() << "通过网卡" << m_multicastIface.humanReadableName() << "加入组播组成功";
        // 设置组播发送的接口(确保发送也走同一网卡) 可不设置
        udpsocket->setMulticastInterface(m_multicastIface);
     } else{
        qWarning() << "指定网卡加入组播组失败:" << udpsocket->errorString();
     }
 }
相关推荐
谭欣辰14 小时前
详细讲解 C++ 状压 DP
开发语言·c++·动态规划
William_wL_14 小时前
【C++】stack和queue的使用和实现(附加deque的简单介绍)
开发语言·c++
山甫aa14 小时前
二叉树遍历----从零开始的数据结构
数据结构·c++·二叉树
忡黑梨15 小时前
eNSP_ACL原理及应用
运维·服务器·网络·tcp/ip·github·负载均衡
摸鱼仙人~15 小时前
HTTP 状态码系统拆解
网络·网络协议·http
白晨并不是很能熬夜15 小时前
【RPC】第 1 篇:全景篇 — 一次 RPC 调用的完整旅程
java·网络·后端·网络协议·面试·rpc·java-zookeeper
1892280486115 小时前
EMMC32G-M525闪存EMMC32G-T527
网络·人工智能
APIshop15 小时前
1688 跨境寻源通详情接口深度解析:从接入到实战
前端·网络·chrome
SDWAN_Cheap15 小时前
深入解析SD-WAN专线的六大核心能力:以零售连锁行业为例
网络·零售
cpp_250115 小时前
P2249 【深基13.例1】查找
数据结构·c++·算法·题解·二分·洛谷