https://blog.csdn.net/only_write_bug/article/details/159115776?spm=1011.2415.3001.5331
目录
一、组播介绍
组播(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();
}
}