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();
     }
 }
相关推荐
watson_pillow13 小时前
c++ 协程的初步理解
开发语言·c++
故事和你9113 小时前
洛谷-算法1-2-排序2
开发语言·数据结构·c++·算法·动态规划·图论
白毛大侠14 小时前
理解 Go 接口:eface 与 iface 的区别及动态性解析
开发语言·网络·golang
黎相思14 小时前
音乐播放器
qt
Tanecious.15 小时前
蓝桥杯备赛:Day6-B-小紫的劣势博弈 (牛客周赛 Round 85)
c++·蓝桥杯
流云鹤15 小时前
Codeforces Round 1090 (Div. 4)
c++·算法
小菜鸡桃蛋狗15 小时前
C++——string(上)
开发语言·c++
wljy115 小时前
第十三届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(个人见解,已完结)
c语言·c++·算法·蓝桥杯·stl
清空mega16 小时前
C++中关于数学的一些语法回忆(2)
开发语言·c++·算法
想唱rap16 小时前
线程池以及读写问题
服务器·数据库·c++·mysql·ubuntu