网站首页 > 技术文章 正文
1. UDP组播的特性
UDP组播是主机之间“一对一组”的通信模式,当多个客户端加入由一个组播地址定义的多播组之后,客户端向组播地址和端口发送的UDP数据报,组内成员都可以接收到,其功能类似于QQ群。
组播报文的目的地址使用D类IP地址,D类地址不能出现在IP报文的源IP地址字段。用同一个IP多播地址接收多播数据报的所有主机构成了一个组,称为多播组(或组播组)。所有的信息接收者都加入到一个组内,并且一旦加入之后,流向组地址的数据报立即开始向接收者传输,组中的所有成员都能接收到数据报。组中的成员是动态的,主机可以在任何时间加入和离开组。
所以,采用UDP组播必须使用一个组播地址。组播地址是D类IP地址,有特定的地址段。多播组可以是永久的也可以是临时的。多播组地址中,有一部分由官方分配,称为永久多播组。永久多播组保持不变的是它的IP地址,组中的成员构成可以发生变化。永久多播组中成员的数量可以是任意的,甚至可以为零。那些没有保留下来的供永久多播组使用的IP组播地址,可以被临时多播组利用。
关于组播IP地址,有如下的一些约定:
- 224.0.0.0?224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用;
- 224.0.1.0?224.0.1.255是公用组播地址,可以用于Internet;
- 224.0.2.0?238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
- 239.0.0.0?239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
所以,若是在家庭或办公室局域网内测试UDP组播功能,可以使用的组播地址范围是239.0.0.0 ?239.255.255.255。
QUdpSocket支持UDP组播,joinMulticastGroup()函数使主机加入一个多播组,leaveMulticastGroup()函数使主机离开一个多播组,UDP组播的特点是使用组播地址,其他的端口绑定、数据报 收发等功能的实现与单播UDP完全相同。
2. UDP组播实例程序的功能
设计一个UDP组播实例程序,进行组播通信。下图是运行的程序界面。程序加入地址为239.255.10.1的多播组,绑定端口35300进行通信。
从图中可以看到,程序可以发送和接收组播数据报,且在自己主机上发出的数据报,自己也可以接收到。
3. 组播功能的程序实现
程序的主窗口是基于QMainWindow的类MainWindow,界面由UI设计器设计,其类定义如下(忽略UI设计器生成的槽函数):
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QLabel *LabSocketState;//状态栏标签
QUdpSocket *udpSocket;
QHostAddress groupAddress; //组播地址
QString getLocalIP();//获取本机 IP 地址
private slots:
//自定义槽函数
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onSocketReadyRead();//读取 socket 传入的数据
};
其中定义了一个QHostAddress类型变量groupAddress,用于记录组播地址。下面是MainWindow的构造函数的代码:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
LabSocketState=new QLabel("Socket 状态:");
LabSocketState->setMinimumWidth(200);
ui->statusbar->addWidget(LabSocketState);
QString localIP=getLocalIP();//本地主机名
this->setWindowTitle(this->windowTitle()+" 本机IP: "+localIP);
udpSocket=new QUdpSocket(this);
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(udpSocket->state());
connect(udpSocket,SIGNAL(readyRead()), this,SLOT(onSocketReadyRead()));
}
其中使用了QUdpSocket::setSocketOption()函数,对socket进行参数设置。
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
将socket的QAbstractSocket::MulticastTtlOption值设置为1。MulticastTtlOption是UDP组播的数据报的生存期,数据报每跨1个路由会减1。缺省值为1,表示多播数据报只能在同一路由下的局域网内传播。
要进行UDP组播通信,UDP客户端必须先加入UDP多播组,也可以随时退出多播组。主窗口上的“加入组播”和“退出组播”按钮的代码如下:
void MainWindow::on_actionStart_triggered()
{//加入组播
QString IP=ui->comboIP->currentText();
groupAddress=QHostAddress(IP);//多播组地址
quint16 groupPort=ui->spinPort->value(); //端口
if(udpSocket->bind(QHostAddress::AnyIPv4, groupPort, QUdpSocket::ShareAddress)){
udpSocket->joinMulticastGroup(groupAddress); //加入多播组
ui->plainTextEdit->appendPlainText("**加入组播成功");
ui->plainTextEdit->appendPlainText("**组播地址 IP: " + IP);
ui->plainTextEdit->appendPlainText("**绑定端口: "+QString::number(groupPort));
ui->actionStart->setEnabled(false);
ui->actionStop->setEnabled(true);
ui->comboIP->setEnabled(false);
}else{
ui->plainTextEdit->appendPlainText ("**绑定端口失败");
}
}
void MainWindow::on_actionStop_triggered()
{//退出组播
udpSocket->leaveMulticastGroup (groupAddress);//退出组播
udpSocket->abort(); //解除绑定
ui->actionStart->setEnabled(true);
ui->actionStop->setEnabled(false);
ui->comboIP->setEnabled(true);
ui->plainTextEdit->appendPlainText("** 已退出组播,解除端口绑定");
}
加入组播之前,必须先绑定端口,绑定端口的语句是:
udpSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress)
这里指定地址为QHostAddress::AnyIPv4,端口为多播组统一的一个端口。
使用QUdpSocket::joinMulticastGroup()函数加入多播组:
udpSocket->joinMulticastGroup(groupAddress);
多播组地址groupAddress由界面上的组合框里输入。注意,局域网内的组播地址的范围是239.0.0.0?239.255.255.255,绝对不能使用本机地址作为组播地址。
退出多播组,使用QUdpSocket::leaveMulticastGroup()函数:
udpSocket->leaveMulticastGroup(groupAddress);
加入多播组后,发送组播数据报也是使用writeDatagram()函数,只是目标地址使用的是组播地址,在readyRead()信号的槽函数里用readDatagram()读取数据报。下面是发送和读取数据报的代码:
void MainWindow::on_btnSend_clicked()
{ //发送组播消息
quint16 groupPort=ui->spinPort->value();
QString msg=ui->edtMsg->text();
QByteArray datagram=msg.toUtf8();
udpSocket->writeDatagram(datagram,groupAddress,groupPort);
ui->plainTextEdit->appendPlainText("[multicst] "+msg);
ui->edtMsg->clear();
ui->edtMsg->setFocus();
}
void MainWindow::onSocketReadyRead()
{//读取数据报
while(udpSocket->hasPendingDatagrams()){
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);
QString str=datagram.data();
QString peer=" [From "+peerAddr.toString()+": "+QString::number(peerPort)+"]";
ui->plainTextEdit->appendPlainText(peer+str);
}
}
————————————————
觉得有用的话请关注点赞,谢谢您的支持!
对于本系列文章相关示例完整代码有需要的朋友,可关注并在评论区留言!
猜你喜欢
- 2024-12-24 Excel VBA 用户窗体设置/一步一步代你设计EXCEL用户+密码登录界面
- 2024-12-24 家庭收支理财管理系统 Access数据库系统课程设计制作实例
- 2024-12-24 Qt 2D绘图:图形视图框架的事件处理与传播
- 2024-12-24 MFC界面库BCGControlBar v32.1 - 可视化管理器和主题升级
- 2024-12-24 DJYGUI系列文章九:GDD消息系统 gd信息
- 2024-12-24 Qt设备识别(简单的密钥生成器) qt设备管理系统
- 2024-12-24 Access开发的《财务经济管理系统》
- 2024-12-24 初级开发人员告诉我:OO 设计模式太复杂而且没用
- 2024-12-24 从零开始学Qt(89):UDP单播和广播
- 2024-12-24 Qt入门阶段之事件 qtc间期延长的临床意义
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)