14-4_Qt 5.9 C++开发指南_QUdpSocket实现 UDP 通信_UDP组播

文章目录

  • 1. UDP组播的特性
  • 2. UDP 组播实例程序的功能
  • 3. 组播功能的程序实现
  • 4. 源码
    • 4.1 可视化UI设计
    • 4.2 mainwindow.h
    • 4.3 mainwindow.cpp

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 是公用组播地址,可以用于 Intermet;
  • 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 组播实例程序 Samp14_4,在两台计算机上分别运行,进行组播通信。图 14-10是运行于主机 192.168.1.104 上的程序,图 14-11 是运行于主机 192.168.1.106 上的程序。两个主机上的程序都加入地址为239.255.43.21的多播组,绑定端口 35320进行通信。
从图 14-10 和图14-11可以看到,两个 Samp14_4 程序都可以发送和接收组播数据报,且在自已主机上发出的数据报,自己也可以接收到。

在这里插入图片描述

3. 组播功能的程序实现

程序的主窗口是基于 QMainWindow 的类 MainWindow,界面由 UI 设计器设计,其类定义如下(忽略 UI 设计器生成的槽函数):

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include    <QMainWindow>#include    <QUdpSocket>
#include    <QLabel>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTprivate:QLabel  *LabSocketState;QUdpSocket  *udpSocket;//用于与连接的客户端通讯的QTcpSocketQHostAddress    groupAddress;//组播地址QString getLocalIP();//获取本机IP地址
public:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:
//自定义槽函数void    onSocketStateChange(QAbstractSocket::SocketState socketState);void    onSocketReadyRead();//读取socket传入的数据
...private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

其中定义了一个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);//用于与连接的客户端通讯的QTcpSocket
//Multicast路由层次,1表示只在同一局域网内//组播TTL: 生存时间,每跨1个路由会减1,多播无法跨过大多数路由所以为1//默认值是1,表示数据包只能在本地的子网中传送。udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
//    udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,ui->spinTTL->value());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_actStart_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->actStart->setEnabled(false);ui->actStop->setEnabled(true);ui->comboIP->setEnabled(false);}elseui->plainTextEdit->appendPlainText("**绑定端口失败");
}void MainWindow::on_actStop_triggered()
{//退出组播udpSocket->leaveMulticastGroup(groupAddress);//退出组播udpSocket->abort(); //解除绑定ui->actStart->setEnabled(true);ui->actStop->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_btnMulticast_clicked()
{//发送组播消息quint16     groupPort=ui->spinPort->value();QString  msg=ui->editMsg->text();QByteArray  datagram=msg.toUtf8();udpSocket->writeDatagram(datagram,groupAddress,groupPort);
//    udpSocket->writeDatagram(datagram.data(),datagram.size(),
//                     groupAddress,groupPort);ui->plainTextEdit->appendPlainText("[multicst] "+msg);ui->editMsg->clear();ui->editMsg->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);//        udpSocket->readDatagram(datagram.data(),datagram.size());QString str=datagram.data();QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";ui->plainTextEdit->appendPlainText(peer+str);}
}

4. 源码

4.1 可视化UI设计

在这里插入图片描述

4.2 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include    <QMainWindow>#include    <QUdpSocket>
#include    <QLabel>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTprivate:QLabel  *LabSocketState;QUdpSocket  *udpSocket;//用于与连接的客户端通讯的QTcpSocketQHostAddress    groupAddress;//组播地址QString getLocalIP();//获取本机IP地址
public:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:
//自定义槽函数void    onSocketStateChange(QAbstractSocket::SocketState socketState);void    onSocketReadyRead();//读取socket传入的数据
//void on_actStart_triggered();void on_actStop_triggered();void on_actClear_triggered();void on_actHostInfo_triggered();void on_btnMulticast_clicked();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

4.3 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include    <QtNetwork>QString MainWindow::getLocalIP()
{QString hostName=QHostInfo::localHostName();//本地主机名QHostInfo   hostInfo=QHostInfo::fromName(hostName);QString   localIP="";QList<QHostAddress> addList=hostInfo.addresses();//if (!addList.isEmpty())for (int i=0;i<addList.count();i++){QHostAddress aHost=addList.at(i);if (QAbstractSocket::IPv4Protocol==aHost.protocol()){localIP=aHost.toString();break;}}return localIP;
}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);//用于与连接的客户端通讯的QTcpSocket
//Multicast路由层次,1表示只在同一局域网内//组播TTL: 生存时间,每跨1个路由会减1,多播无法跨过大多数路由所以为1//默认值是1,表示数据包只能在本地的子网中传送。udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
//    udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,ui->spinTTL->value());connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(udpSocket->state());connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}MainWindow::~MainWindow()
{udpSocket->abort();delete udpSocket;delete ui;
}void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{switch(socketState){case QAbstractSocket::UnconnectedState:LabSocketState->setText("scoket状态:UnconnectedState");break;case QAbstractSocket::HostLookupState:LabSocketState->setText("scoket状态:HostLookupState");break;case QAbstractSocket::ConnectingState:LabSocketState->setText("scoket状态:ConnectingState");break;case QAbstractSocket::ConnectedState:LabSocketState->setText("scoket状态:ConnectedState");break;case QAbstractSocket::BoundState:LabSocketState->setText("scoket状态:BoundState");break;case QAbstractSocket::ClosingState:LabSocketState->setText("scoket状态:ClosingState");break;case QAbstractSocket::ListeningState:LabSocketState->setText("scoket状态:ListeningState");}
}void MainWindow::onSocketReadyRead()
{//读取数据报while(udpSocket->hasPendingDatagrams()){QByteArray   datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress    peerAddr;quint16 peerPort;udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);//        udpSocket->readDatagram(datagram.data(),datagram.size());QString str=datagram.data();QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";ui->plainTextEdit->appendPlainText(peer+str);}
}void MainWindow::on_actStart_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->actStart->setEnabled(false);ui->actStop->setEnabled(true);ui->comboIP->setEnabled(false);}elseui->plainTextEdit->appendPlainText("**绑定端口失败");
}void MainWindow::on_actStop_triggered()
{//退出组播udpSocket->leaveMulticastGroup(groupAddress);//退出组播udpSocket->abort(); //解除绑定ui->actStart->setEnabled(true);ui->actStop->setEnabled(false);ui->comboIP->setEnabled(true);ui->plainTextEdit->appendPlainText("**已退出组播,解除端口绑定");
}void MainWindow::on_actClear_triggered()
{ui->plainTextEdit->clear();
}void MainWindow::on_actHostInfo_triggered()
{QString hostName=QHostInfo::localHostName();//本地主机名ui->plainTextEdit->appendPlainText("本机主机名:"+hostName+"\n");QHostInfo   hostInfo=QHostInfo::fromName(hostName);QList<QHostAddress> addList=hostInfo.addresses();//if (!addList.isEmpty())for (int i=0;i<addList.count();i++){QHostAddress aHost=addList.at(i);if (QAbstractSocket::IPv4Protocol==aHost.protocol()){QString IP=aHost.toString();ui->plainTextEdit->appendPlainText("本机IP地址:"+aHost.toString());if (ui->comboIP->findText(IP)<0)ui->comboIP->addItem(IP);}}
}void MainWindow::on_btnMulticast_clicked()
{//发送组播消息quint16     groupPort=ui->spinPort->value();QString  msg=ui->editMsg->text();QByteArray  datagram=msg.toUtf8();udpSocket->writeDatagram(datagram,groupAddress,groupPort);
//    udpSocket->writeDatagram(datagram.data(),datagram.size(),
//                     groupAddress,groupPort);ui->plainTextEdit->appendPlainText("[multicst] "+msg);ui->editMsg->clear();ui->editMsg->setFocus();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/78664.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧工地云平台源码,基于微服务+Java+Spring Cloud +UniApp +MySql开发

智慧工地可视化系统利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;通过工地中台、三维建模服务、视频AI分析服务等技术支撑&#xff0c;实现智慧工地高精度动态仿真&#xff0c;趋势分析、预测、模拟&#xff0c;建设智能化、标准化的智慧工地…

uni-app:实现列表单选功能

效果图&#xff1a; 核心解析&#xff1a; 一、 <view class"item_all" v-for"(item, index) in info" :key"index"><view classposition parameter-info text-over :classitem.checked?"checked_parameter":""…

ubuntu 暂时不能解析域名 解决办法

需要修改系统DNS 打开终端&#xff1a;输入 sudo vi /etc/resolv.conf 回车 在打开的配置文件中添加DNS信息 nameserver 114.114.114.114 nameserver 8.8.8.8 保存退出&#xff0c;重启系统即可。

【Nacos篇】Nacos基本操作及配置

官方文档&#xff1a;https://nacos.io/zh-cn/docs/v2/ecology/use-nacos-with-spring-cloud.html 前置条件&#xff1a;SpringCloud脚手架 单机模式下的Nacos控制台&#xff1a; <dependencies><!-- Registry 注册中心相关 --><dependency><groupId>…

《golang设计模式》第一部分·创建型模式-04-抽象工厂模式(Abstract Factory)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 1.1 角色 AbstractFactory&#xff08;抽象工厂&#xff09;&#xff1a;它声明了一组用于创建产品的方法&#xff0c;每一个方法对应一种产品。ConcreteFactory&#xff08;具体工厂&#xf…

系统架构设计高级技能 · 软件可靠性分析与设计(三)【系统架构设计师】

系列文章目录 系统架构设计高级技能 软件架构概念、架构风格、ABSD、架构复用、DSSA&#xff08;一&#xff09;【系统架构设计师】 系统架构设计高级技能 系统质量属性与架构评估&#xff08;二&#xff09;【系统架构设计师】 系统架构设计高级技能 软件可靠性分析与设计…

HCIP 三层交换机

一、实现VLAN间通信 在传统的交换机组网中&#xff0c;默认所有网络都处于同一个广播域&#xff0c;带来了许多问题&#xff0c;VLAN技术的提出&#xff0c;满足了二层组网隔离广播域需求&#xff0c;使得属于不同的VLAN间网络无法通信&#xff0c;但不同VLAN之间又存在着互相…

MybatisPlus存在 sql 注入漏洞(CVE-2023-25330)解决办法

首先我们了解下这个漏洞是什么&#xff1f; MyBatis-Plus TenantPlugin 是 MyBatis-Plus 的一个为多租户场景而设计的插件&#xff0c;可以在 SQL 中自动添加租户 ID 来实现数据隔离功能。 MyBatis-Plus TenantPlugin 3.5.3.1及之前版本由于 TenantHandler#getTenantId 方法在…

快速排序【Java算法】

文章目录 1. 概念2. 思路3. 代码实现 1. 概念 快速排序是一种比较高效的排序算法&#xff0c;采用 “分而治之” 的思想&#xff0c;通过多次比较和交换来实现排序&#xff0c;在一趟排序中把将要排序的数据分成两个独立的部分&#xff0c;对这两部分进行排序使得其中一部分所有…

Python web实战之Django用户认证详解

关键词&#xff1a; Python Web 开发、Django、用户认证、实战案例 概要 今天来探讨一下 Django 的用户认证吧&#xff01;在这篇文章中&#xff0c;我将为大家带来一些有关 Django 用户认证的最佳实践。 1. Django 用户认证 在开发 Web 应用程序时&#xff0c;用户认证是一个…

05 Ubuntu下安装.deb安装包方式安装vscode,snap安装Jetbrains产品等常用软件

使用deb包安装类型 deb包指的其实就是debian系统&#xff0c;ubuntu系统是基于debian系统的发行版。 一般我们会到需要的软件官网下载deb安装包&#xff0c;然后你既可以采用使用“软件安装”打开的方法来进行安装&#xff0c;也可以使用命令行进行安装。我推荐后者&#xff…

Mr. Cappuccino的第58杯咖啡——MacOS配置Maven和Java环境

MacOS配置Maven和Java环境 查看Mac使用的是哪个shell下载并准备Maven下载Maven配置前准备 下载并安装JDK下载JDK安装JDK 配置Maven和Java环境添加配置加载配置 验证环境 查看Mac使用的是哪个shell echo $SHELL如果使用的是bash&#xff0c;则使用以下命令 open ~/.bash_profi…

六、ESP32数码管显示数字

1. 本节课的成功 2. 数码管 为什么会亮呢? 答:里面就是LED灯

【雕爷学编程】Arduino动手做(180)---Seeeduino Lotus开发板2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

iperf3-性能测试

iperf3-性能测试 安装1.apt安装2.源码安装 使用方法iperf原理测试参考文档性能测试客户端服务端 官方文档&#xff1a;https://iperf.fr/iperf-doc.php 安装 1.apt安装 sudo apt-get install iperf32.源码安装 # 按照官方说明安装 ./configure make sudo make install执行编…

element-ui - $prompt非空验证

//点击删除按钮 delStoreFun(data) { let than this; this.$prompt(删除门店请填写备注, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, inputValidator: (value) > { //非空验证 if (!value) { return 输入不能为空 } }, }).then(({ value }) > { delS…

CNN成长路:从AlexNet到EfficientNet(02)

一、说明 在~10年的深度学习中&#xff0c;进步是多么迅速&#xff01;早在 2012 年&#xff0c;Alexnet 在 ImageNet 上的准确率就达到了 63.3% 的 Top-1。现在&#xff0c;我们超过90%的EfficientNet架构和师生训练&#xff08;teacher-student&#xff09;。 二、第一阶段 …

笔记本WIFI连接无网络【实测有效解决方案,不用重启电脑】

笔记本Wifi连接无网络实测有效解决方案 问题描述&#xff1a; 笔记本买来一段时间后&#xff0c;WIFI网络连接开机一段时间还正常连接&#xff0c;但是过一段时间显示网络连接不上解决方案&#xff1a; 1.编写网络重启bat脚本&#xff0c;将以下内容写到文本文件&#xff0c;把…

最优化:建模、算法与理论

最优化&#xff1a;建模、算法与理论 目前在学习 最优化&#xff1a;建模、算法与理论这本书&#xff0c;来此记录一下&#xff0c;顺便做一些笔记&#xff0c;在其中我也会加一些自己的理解&#xff0c;尽量写的不会那么的条条框框&#xff08;当然最基础的还是要有&#xff…

C#利用自定义特性以及反射,来提大型项目的开发的效率

在大型项目的开发过程中&#xff0c;需要多人协同工作&#xff0c;来加速项目完成进度。 比如一个软件有100个form&#xff0c;分给100个人来写&#xff0c;每个人完成自己的Form.cs的编写之后&#xff0c;要在Mainform调用自己写的Form。 如果按照正常的Form form1 new For…