一、UDP单播、广播和组播的说明
UDP是不可靠、无连接的,所以划分为发送方和接收方更好理解
1、单播
UDP是无连接的,进行单播通信时,只需要绑定接收方端口就行,发送方直接通过接收方的ip和绑定的端口进行通信。发送方可以绑定端口也可以不用绑定端口,不绑定端口的话,系统会随机分配端口。
对于多网卡来说,需要指定网卡,绑定一个网卡的ip。如果不进行显式的绑定操作,QUdpSocket
对象将会使用默认的绑定方式,自动选择一个可用的 ip 地址进行绑定。这种情况下,数据包可能通过任意一个可用的网络接口发送出去。
2、广播
对于只有1个网卡的主机来说,可以不用显示绑定ip,发送方直接发送广播就行,接收方绑定广播端口就行,这样才能看到收到的消息。
对于多网卡来说,要指定唯一的网卡并且在广播前要绑定广播端口,ip可以不用绑定,系统分配任意一个ip ,相当于 QHostAddress::AnyIPv4。
3、组播
对于只有1个网卡的主机来说,可以不用绑定ip,直接绑定端口后加入组播就行。系统分配任意一个ip ,相当于 QHostAddress::AnyIPv4。
对于多网卡来说,要指定唯一的网卡并且在加入组播前要绑定组播端口,ip可以不用绑定,系统分配任意一个ip ,相当于 QHostAddress::AnyIPv4。
注意:指定网卡不等于指定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组播的几个问题https://blog.csdn.net/tom06/article/details/52163665?spm=1001.2014.3001.5506
UDP多播/组播通信,同一局域网下的两台机器通信接收不到数据https://blog.csdn.net/qq_43290013/article/details/117288296?spm=1001.2014.3001.5506
QT读取网卡列表多网卡绑定组播网卡https://blog.csdn.net/qq_30727593/article/details/127441711?spm=1001.2014.3001.5506文章来源:https://www.toymoban.com/news/detail-848572.html
三、效果与代码
文章来源地址https://www.toymoban.com/news/detail-848572.html
#include "widget.h"
#include "ui_widget.h"
/*注意的问题:
问题一:
udp在同一局域网通信,如果一台主机收不到消息,只能本机收发,可以指定网口,删除其他网卡(保留直连网卡),关闭防火墙等操作。
问题二:
使用了不同的套接字对象。单播通信使用的是socket对象,而组播通信使用的是groupSocket对象。
在单播通信成功后,如果你立即尝试进行组播通信,可能会导致问题,因为在组播通信之前,还需要解除socket对象的绑定,然后重新绑定到组播端口。
同样地,在组播通信成功后,如果要切换回单播通信,需要先解除groupSocket对象的绑定,然后重新绑定到单播端口。
*/
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//构造udp套接字
socket = new QUdpSocket;
groupSocket = new QUdpSocket;
ifaceList=QNetworkInterface::allInterfaces();
for(int i=0;i<ifaceList.count();i++)
{
QNetworkInterface var=ifaceList.at(i);
ui->nifComboBox->addItem(var.humanReadableName());
// qDebug() << var.humanReadableName() << ":" << ifaceList.at(i);
}
//本地主机名
QString hostName = QHostInfo::localHostName();
//获取本机IP地址
hostIp = QHostAddress(QHostAddress::LocalHost);
QList<QHostAddress> list = QNetworkInterface::allAddresses();
foreach (QHostAddress address, list) {
if (address.protocol() == QAbstractSocket::IPv4Protocol && !address.isLoopback()) {
qDebug() << "Local IP Address: " << address.toString();
this->ui->ipBox->setText(address.toString());
break;
}
}
if (list.isEmpty()) {
qDebug() << "No network interfaces found.";
} else {
qDebug() << "Local Loopback Address: " << QHostAddress::LocalHost;
}
//组播的数据的生存期,数据报没跨1个路由就会减1.表示多播数据报只能在同一路由下的局域网内传播
groupSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
//当socket读缓存有数据,会立即触发readyRead
connect(socket, &QUdpSocket::readyRead, this, &Widget::recvData);
connect(groupSocket, &QUdpSocket::readyRead, this, &Widget::groupRecvData);
}
Widget::~Widget()
{
delete ui;
}
//单播接收消息
void Widget::recvData()
{
char buf[1024] = {0};
socket->readDatagram(buf, sizeof (buf), &addr, &port);
// 使用正则表达式去掉前缀
QRegularExpression re("^::ffff:");
QString targetIpAddr = addr.toString();
targetIpAddr = targetIpAddr.replace(re, "");
ui->targetPortBox->setValue(port);
ui->targetIpBox->setText(targetIpAddr);
QString prefixText = addr.toString() + ":" + QString::number(port) + ":" ;
prefixText = QString("<font color='blue'>%1</font>").arg(prefixText);
ui->recvEdit->append(prefixText + QString(buf));
}
//绑定端口
void Widget::on_bindBtn_clicked()
{
// 解除groupSocket的绑定
groupSocket->abort();
//本机UDP端口
hostPort = ui->bindPortBox->value();
if (socket->bind(hostPort)){//绑定监听端口成功
ui->bindBtn->setDisabled(true);
ui->recvEdit->append("***已成功绑定端口:"+QString::number(socket->localPort()));
}else{
ui->recvEdit->append("***绑定失败***");
}
}
void Widget::on_sendBtn_clicked()
{
socket->writeDatagram(ui->sendEdit->toPlainText().toLatin1(),QHostAddress(ui->targetIpBox->text()),ui->targetPortBox->text().toInt());
}
void Widget::on_disbindBtn_clicked()
{
socket->abort(); //不能解除绑定
ui->recvEdit->append("***已解除绑定***");
ui->bindBtn->setDisabled(false);
// delete socket;
}
void Widget::on_clearRecvBtn_clicked()
{
ui->recvEdit->clear();
}
void Widget::on_clearSendBtn_clicked()
{
ui->sendEdit->clear();
}
//加入组播
void Widget::on_joinBtn_clicked()
{
QString groupIp = ui->multiIpBox->text();
groupAddr = QHostAddress(groupIp);
groupPort = ui->mulPortBox->value();
// // 解除socket的绑定
// socket->abort();
//加入组播之前,必须先绑定端口,端口为多播组统一的一个端口。
if(groupSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint))
{
if(ui->nifComboBox->currentIndex() != 0){
groupSocket->setMulticastInterface(ifaceList.at(ui->nifComboBox->currentIndex() - 1));
}
//加入组播
groupSocket->joinMulticastGroup(groupAddr,ifaceList.at(ui->nifComboBox->currentIndex() - 1));
ui->joinBtn->setDisabled(true);
ui->recvEdit->append("**加入组播成功");
ui->recvEdit->append("**当前网卡:"+ui->nifComboBox->currentText());
ui->recvEdit->append("**组播IP: "+ groupIp);
ui->recvEdit->append("**绑定端口: "+QString::number(groupPort));
}
else
{
ui->recvEdit->append("**绑定端口失败");
}
}
void Widget::on_exitBtn_clicked()
{
//退出组播
groupSocket->leaveMulticastGroup(groupAddr);
//解除绑定
groupSocket->abort();
ui->joinBtn->setDisabled(false);
ui->recvEdit->append("**已退出组播");
}
void Widget::on_broadSendBtn_clicked()
{
groupSocket->writeDatagram(ui->sendEdit->toPlainText().toUtf8(),groupAddr,groupPort);
}
//组播接收消息
void Widget::groupRecvData()
{
char buf[1024] = {0};
groupSocket->readDatagram(buf, sizeof (buf), &addr, &port);
QString prefixText = addr.toString() + ":" + QString::number(port) + ":" ;
prefixText = QString("<font color='blue'>%1</font>").arg(prefixText);
ui->recvEdit->append(prefixText + QString(buf));
}
到了这里,关于Qt实现UDP单播和组播功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!