C++ Qt开发:QUdpSocket网络通信组件
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QUdpSocket组件实现基于UDP的网络通信功能。
与QTcpSocket组件功能类似,QUdpSocket组件是 Qt 中用于实现用户数据报协议(UDP,User Datagram Protocol)通信的类。UDP 是一种无连接的、不可靠的数据传输协议,它不保证数据包的顺序和可靠性,但具有低延迟和简单的特点。
以下是 QUdpSocket 类的完整函数及其简要解释:
| 函数 | 描述 |
|---|---|
QUdpSocket(QObject *parent = nullptr) | 构造函数,创建一个新的 QUdpSocket 对象。 |
~QUdpSocket() | 析构函数,释放 QUdpSocket 对象及其资源。 |
void bind(const QHostAddress &address, quint16 port, BindMode mode = DefaultForPlatform) | 将套接字绑定到指定的本地地址和端口。 |
void close() | 关闭套接字。 |
bool joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface = QNetworkInterface()) | 加入多播组。 |
bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface = QNetworkInterface()) | 离开多播组。 |
qint64 pendingDatagramSize() const | 返回下一个待读取的数据报的大小。 |
qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 读取数据报。 |
QByteArray readDatagram(qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 读取数据报,返回 QByteArray 对象。 |
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port) | 发送数据报。 |
qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &address, quint16 port) | 发送数据报,接受 QByteArray 对象。 |
QAbstractSocket::SocketState state() const | 返回套接字的当前状态。 |
QAbstractSocket::SocketType socketType() const | 返回套接字的类型。 |
bool isValid() const | 如果套接字有效,则返回 true;否则返回 false。 |
int error() const | 返回套接字的当前错误代码。 |
QHostAddress localAddress() const | 返回本地地址。 |
quint16 localPort() const | 返回本地端口。 |
int readBufferSize() const | 返回读取缓冲区的大小。 |
void setReadBufferSize(int size) | 设置读取缓冲区的大小。 |
QNetworkInterface multicastInterface() const | 返回多播组的网络接口。 |
void setMulticastInterface(const QNetworkInterface &iface) | 设置多播组的网络接口。 |
bool hasPendingDatagrams() const | 如果有待读取的数据报,则返回 true;否则返回 false。 |
bool isReadable() const | 如果套接字可读,则返回 true;否则返回 false。 |
bool isWritable() const | 如果套接字可写,则返回 true;否则返回 false。 |
bool setSocketDescriptor(int socketDescriptor, QUdpSocket::SocketState socketState = ConnectedState, QIODevice::OpenMode openMode = ReadWrite) | 设置套接字描述符。 |
int socketDescriptor() const | 返回套接字描述符。 |
bool waitForReadyRead(int msecs = 30000) | 等待套接字可读取数据。 |
bool waitForBytesWritten(int msecs = 30000) | 等待套接字已写入指定字节数的数据。 |
void ignoreSslErrors(const QList<QSslError> &errors) | 忽略 SSL 错误。 |
void abort() | 强制关闭套接字。 |
QNetworkProxy proxy() const | 返回套接字的代理设置。 |
void setProxy(const QNetworkProxy &networkProxy) | 设置套接字的代理设置。 |
QString errorString() const | 返回套接字的错误消息字符串。 |
这些函数提供了在 UDP 通信中使用 QUdpSocket 的各种功能,包括绑定、发送和接收数据报、设置和获取套接字的状态等。
1.1 初始化部分
在初始化部分我们首先通过new QUdpSocket来实现创建UDP对象,QUdpSocket 构造函数的函数原型如下:
QUdpSocket::QUdpSocket(QObject * parent = nullptr)
如上构造函数创建一个新的 QUdpSocket 对象。如果提供了 parent 参数,则会将新创建的 QUdpSocket 对象添加到 parent 对象的子对象列表中,并且在 parent 对象被销毁时自动销毁 QUdpSocket 对象。如果没有提供 parent 参数,则 QUdpSocket 对象将不会有父对象,并且需要手动管理其生命周期。
初始化结束后,则下一步需要调用bind(),bind() 函数是 QUdpSocket 类的一个成员函数,用于将套接字绑定到特定的本地地址和端口。它的函数原型如下:
void QUdpSocket::bind(const QHostAddress &address, quint16 port, BindMode mode = DefaultForPlatform)
address:要绑定的本地地址,通常是QHostAddress::Any,表示绑定到所有可用的网络接口。port:要绑定的本地端口号。mode:绑定模式,指定套接字的行为。默认值是DefaultForPlatform,表示使用平台默认的绑定模式。
该函数允许 QUdpSocket 在本地网络接口上监听传入的数据报。一旦调用了 bind() 函数,QUdpSocket 就可以接收来自指定地址和端口的数据报。
在调用 bind() 函数之后,如果成功绑定了指定的地址和端口,套接字将处于 BoundState 状态。如果出现错误,可以通过检查 error() 函数获取错误代码,并通过 errorString() 函数获取错误消息。
接着我们通过connect()函数依次绑定套接字到stateChanged状态改变信号,以及readyRead()读取信号上,这段初始化代码如下所示;
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);udpSocket=new QUdpSocket(this);// 生成随机整数 包含2000 - 不包含65534int randomInt = QRandomGenerator::global()->bounded(2000, 65534);if(udpSocket->bind(randomInt)){this->setWindowTitle(this->windowTitle() + " | 地址: " + getLocalAddress() + " 绑定端口:" + QString::number(udpSocket->localPort()));}connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(udpSocket->state());connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}
接着切换到读取信号所对应的槽函数上,onSocketReadyRead是我们自定义的一个槽,该槽函数功能如下所示;
// 读取收到的数据报
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="[消息来自 " + peerAddr.toString()+":"+QString::number(peerPort)+"] | ";ui->plainTextEdit->appendPlainText(peer+str);}
}
首先在代码中调用pendingDatagramSize函数,pendingDatagramSize() 是 QUdpSocket 类的一个成员函数,用于获取下一个待读取的数据报的大小。它的函数原型如下:
qint64 QUdpSocket::pendingDatagramSize() const
该函数返回一个 qint64 类型的值,表示下一个待读取的数据报的大小(以字节为单位)。如果没有待读取的数据报,或者发生了错误,该函数将返回 -1。
通常,可以在调用 readDatagram() 函数之前调用 pendingDatagramSize() 函数来获取下一个待读取的数据报的大小。这样可以为数据缓冲区分配正确大小的空间,以确保完整地读取数据报。
当有了待读取字节后,接着就可以直接通过调用readDatagram函数来从套接字中读取数据报,readDatagram() 是 QUdpSocket 类的一个成员函数,它有几个重载形式,其中最常用的是:
qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize, QHostAddress * address = nullptr, quint16 * port = nullptr)
该函数用于读取数据报并将其存储到指定的缓冲区 data 中,最多读取 maxSize 个字节的数据。可选参数 address 和 port 用于返回数据报的源地址和端口号。如果不需要这些信息,可以将它们设置为 nullptr。
函数返回实际读取的字节数,如果发生错误,返回 -1。要查看错误信息,可以使用 error() 和 errorString() 函数。
另外,还有一个更简单的重载形式:
QByteArray QUdpSocket::readDatagram(qint64 maxSize, QHostAddress * address = nullptr, quint16 * port = nullptr)
这个重载函数直接返回一个 QByteArray 对象,其中包含了读取的数据报。
1.2 单播与广播消息
单播(Unicast)和广播(Broadcast)是网络通信中常见的两种数据传输方式,它们在数据包的传输范围和目标数量上有所不同。
单播(Unicast)
单播是一种一对一的通信方式,其中数据包从一个发送者传输到一个接收者。在单播通信中,数据包只发送到目标主机的网络接口,并且只有目标主机能够接收和处理这个数据包。
- 一对一通信:每个数据包只有一个发送者和一个接收者。
- 目标明确:数据包只发送到特定的目标主机,其他主机不会接收到这个数据包。
- 点到点通信:适用于直接通信的场景,如客户端与服务器之间的通信。
当按钮发送消息被点击后,则是一种单播模式,通常该模式需要得到目标地址与端口号,并通过调用writeDatagram来实现数据的发送,该函数通过传入三个参数,分别是发送字符串,目标地址与目标端口来实现一对一推送。
void MainWindow::on_pushButton_clicked()
{QHostAddress targetAddr(ui->lineEdit_addr->text());QString portString = ui->lineEdit_port->text();quint16 targetPort = portString.toUShort();QString msg=ui->lineEdit_msg->text();QByteArray str=msg.toUtf8();// 发送数据报udpSocket->writeDatagram(str,targetAddr,targetPort);ui->plainTextEdit->appendPlainText("[单播消息] | " + msg);
}
广播(Broadcast)
广播是一种一对多的通信方式,其中数据包从一个发送者传输到同一网络中的所有主机。在广播通信中,数据包被发送到网络中的所有主机,并且所有的主机都能够接收和处理这个数据包。
- 一对多通信:每个数据包有一个发送者,但可以有多个接收者。
- 目标不明确:数据包被发送到网络中的所有主机,不需要知道接收者的具体地址。
- 广播域:在局域网中进行广播,只有在同一广播域内的主机才能接收到广播消息。
- 网络负载:在大型网络中使用广播可能会产生大量的网络流量,影响网络性能。
当按钮广播消息被点击后,则同样是调用writeDatagram函数与,唯一的区别在于第二个参数并未指定地址,而是使用了QHostAddress::Broadcast来代替,意味着只要端口是一致的则对所有的客户推送消息,其他保持不变。
void MainWindow::on_pushButton_2_clicked()
{// 广播地址QString portString = ui->lineEdit_port->text();quint16 targetPort = portString.toUShort();QString msg=ui->lineEdit_msg->text();QByteArray str=msg.toUtf8();udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);ui->plainTextEdit->appendPlainText("[广播消息] | " + msg);
}
读者可自行运行两次客户端,此时的端口将会随机分配,当指定对端端口后就可以向其发送数据,如下图所示;具体实现细节,请参考文章附件。

相关文章:
C++ Qt开发:QUdpSocket网络通信组件
Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QUdpSocket组件实现基于UDP的网络通信…...
微信小程序小白易入门基础教程1
微信小程序 基本结构 页面配置 页面配置 app.json 中的部分配置,也支持对单个页面进行配置,可以在页面对应的 .json 文件来对本页面的表现进行配置。 页面中配置项在当前页面会覆盖 app.json 中相同的配置项(样式相关的配置项属于 app.js…...
D. Tandem Repeats? - 思维 + 双指针
题面 分析 s s s的范围很小,可以 O ( n 2 ) O(n^2) O(n2),在规定复杂度以内来完成枚举所有子串判断是否有满足条件的最大的子串,可以在第一层循环枚举子串长度 d d d,第二层循环枚举左右端点,通过双指针维护区间。对长…...
第十三届蓝桥杯省赛CC++ 研究生组
蓝桥杯2022年第十三届省赛真题-裁纸刀 蓝桥杯2022年第十三届省赛真题-灭鼠先锋 蓝桥杯2022年第十三届省赛真题-质因数个数 求个数,则只需要计数即可。求啥算啥,尽量不要搞多余操作 蓝桥杯2022年第十三届省赛真题-选数异或 蓝桥杯2022年第十三届省赛真题…...
Oracle中的commit与rollback
SQL语言分为五大类: DDL(数据定义语言:DataDefinitionLanguage) - Create、Alter、Drop 这些语句自动提交,无需用Commit提交。 DQL(数据查询语言:DataQueryLanguage) - Select 查询语句不存在是否提交问题。 DML(数据操纵语言:DataManipulationLangua…...
鸿蒙Harmony应用开发—ArkTS声明式开发(画布组件:OffscreenCanvasRenderingContext2D)
使用OffscreenCanvasRenderingContext2D在Canvas上进行离屏绘制,绘制对象可以是矩形、文本、图片等。离屏绘制是指将需要绘制的内容先绘制在缓存区,然后将其转换成图片,一次性绘制到canvas上,加快了绘制速度。 说明: 从…...
Redis如何实现主从复制?主从复制的作用是什么?Redis集群是如何工作的?它有哪些优点和缺点?
Redis如何实现主从复制?主从复制的作用是什么? Redis的主从复制是一种数据复制机制,其中一个Redis实例作为主节点(master),而其他Redis实例作为从节点(slave)。主从复制的实现过程如…...
【Numpy】(2)numpy对象和random模块
numpy.array对象 numpy.array 对象是 NumPy 库的核心,它提供了一种高效的方式来存储和操作同质数据类型的多维数组。每个 numpy.array 对象都有一系列的属性,这些属性提供了关于数组的重要信息。理解这些属性对于有效地使用 NumPy 和进行数据分析是非常…...
[QJS xmake] 非常简单地在Windows下编译QuickJS!
文章目录 前言准备C编译器xmake编译包 工程准备修改版本号第一遍编译第二遍编译效果 前言 quickjs是个很厉害的东西啊,我一直想编译一下的,奈何一直没成功。现在找了点时间成功编译了,写篇文章记录一下。当前版本:2024-1-13 应该…...
Ubuntu双系统/home分区扩容
一、Windows系统中利用磁盘管理分出空闲区域,如果多就多分一些 二、插入安装Ubuntu的U盘启动盘,lenovo电脑F12(其他电脑可选择其他类似方式)选择U盘启动项,然后选择ubuntu,出现安装界面,再选择t…...
0基础学习VR全景平台篇第145篇:图层控件功能
大家好,欢迎观看蛙色VR官方——后台使用系列课程!这期,我们将为大家介绍如何使用图层控件功能。 一.如何使用图层控件功能? 进入作品编辑页面,点击左边的控件后就可以在右边进行相应设置。 二.图层控件有哪些功能&am…...
一文看懂套利的那些事儿
我们经常在投资中都有提到套利策略,经常听到某某套利,或者借用什么套利工具,股票可以套利,基金期货期权也可以套利,套利到底是什么?套利到底是如何运用的? 今天我们一文解读套利市场!…...
【工具】Mermaid + 大模型画流程图
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 引入使用画TCP三次握手了解历史人物 总结 引入 最近看面试文章关于TCP三次握手和…...
内网渗透学习-环境搭建
1、环境搭建测试 虚拟机网络环境配置,模拟外网和内网 主机操作系统网络内网ip外网ip物理主机window10vmnet8192.168.70.1攻击机kali Linuxvmnet8192.168.70.134域控主机win server 2008 r2vmnet0192.168.52.138域成员主机win server 2k3vmnet0192.168.52.141服务器…...
el-progress渐变色
线性渐变喝环形渐变 线性渐变:直接修改样式根据所需颜色修改即可 ::v-deep .el-progress {.el-progress-bar {.el-progress-bar__outer {background: linear-gradient(to right#8075fd, #b87bfe)}.el-progress-bar__inner {background: linear-gradient(to right…...
DHCP在企业网的部署及安全防范
学习目标: 1. DHCP能够解决什么问题? 2. DHCP服务器如何部署? 3. 私接设备会带来什么问题以及如何防范? 给DHCP服务器配置地址: 地址池: DHCP有2种分配模式:全局分配和接口分配 DHCP enable...
列表的常用操作
列表的常用操作(方法) 列表除了可以: 定义使用下标索引获取值 此外列表也提供一些列功能:插入元素删除元素清空元素修改元素统计元素个数 等等功能,这些功能我们都称之为:列表的方法 列表的查询功能&…...
外包2月,技术退步惊现!大专生逆袭大厂,全靠这份神秘资料!
大家好,我是一名大专生,19年通过校招进入湖南某软件公司,从事功能测试工作已近4年。今年8月,我意识到长期舒适的环境让我变得不思进取,技术停滞不前,甚至因此失去了谈了2年的女朋友。我下定决心,…...
考研失败, 学点Java打小工_Day3_卫语句_循环
1 编码规范——卫语句 表达异常分支时,少用if-else方式。 比如成绩判断中对于非法输入的处理: /*>90 <100 优秀>80 <90 良好>70 <80 一般>60 <70 及格<60 不及格*/Testpu…...
网络架构层_服务器上下行宽带
网络架构层_服务器上下行宽带 解释一 云服务器ECS网络带宽的概念、计费、安全及使用限制_云服务器 ECS(ECS)-阿里云帮助中心 网络带宽是指在单位时间(一般指的是1秒钟)内能传输的数据量,带宽数值越大表示传输能力越强,即在单位…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...
qt 双缓冲案例对比
双缓冲 1.双缓冲原理 单缓冲:在paintEvent中直接绘制到屏幕,绘制过程被用户看到 双缓冲:先在redrawBuffer绘制到缓冲区,然后一次性显示完整结果 代码结构 单缓冲:所有绘制逻辑在paintEvent中 双缓冲:绘制…...
Spring Boot SQL数据库功能详解
Spring Boot自动配置与数据源管理 数据源自动配置机制 当在Spring Boot项目中添加数据库驱动依赖(如org.postgresql:postgresql)后,应用启动时自动配置系统会尝试创建DataSource实现。开发者只需提供基础连接信息: 数据库URL格…...
Linux--vsFTP配置篇
一、vsFTP 简介 vsftpd(Very Secure FTP Daemon)是 Linux 下常用的 FTP 服务程序,具有安全性高、效率高和稳定性好等特点。支持匿名访问、本地用户登录、虚拟用户等多种认证方式,并可灵活控制权限。 二、安装与启动 1. 检查是否已…...
用 n8n 提取静态网页内容:从 HTTP Request 到 HTML 节点全解析
n8n 的 HTTP Request HTML 节点组合是个实用又高效的工具。这篇文章就带你一步步搞懂如何用它们提取静态网页内容,重点解析 HTML 节点参数和 CSS 选择器,让你轻松上手 。 一、整体流程概览 我们的目标是从静态网页中提取特定内容,流程分两…...
破解HTTP无状态:基于Java的Session与Cookie协同工作指南
HTTP协议自身是属于“无状态”协议 无状态是指:默认情况下,HTTP协议的客户端和服务器之间的这次通信,和下次通信之间没有直接的关系 但在实际开发中,我们很多时候是需要知道请求之间的关联关系的 上述图中的令牌,通常就…...
Linux环境-通过命令查看zookeeper注册的服务
假设前置条件如下: 1.root权限用户名:zookeeper 2.zookeeper所在服务器地址:168.7.3.254(非真实ip) 3.zookeeper的bin文件路径:/opt/zookeeper/bin 4.确保zookeeper注册中心已启动 查看注册中心服务如下&a…...
