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 个字节的数据。可选参数 addressport 用于返回数据报的源地址和端口号。如果不需要这些信息,可以将它们设置为 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);
}

读者可自行运行两次客户端,此时的端口将会随机分配,当指定对端端口后就可以向其发送数据,如下图所示;具体实现细节,请参考文章附件。

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

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

相关文章

离散制造企业MES与流程企业MES的区别

制造行业根据加工过程管控主要分为两大类&#xff1a;离散型与流程型。 离散型主要是通过对原材料的物理形状改进或组合&#xff0c;使其成为产品并增值&#xff0c;如机械加工、家用电器、电子电气行业等。 流程型则主要是采用物料或化学的方法对原材料进行混合、分离、加热…

11|代理(下):结构化工具对话、Self-Ask with Search以及 Plan and execute代理

在上一讲中&#xff0c;我们深入LangChain程序内部机制&#xff0c;探索了AgentExecutor究竟是如何思考&#xff08;Thought&#xff09;、执行&#xff08;Execute/Act&#xff09;和观察&#xff08;Observe&#xff09;的&#xff0c;这些步骤之间的紧密联系就是代理在推理&…

移除元素(leetcode)

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面…

AI智能客服所需的数据

实现智能客服所需的数据可以分为几个主要类别&#xff0c;这些数据对于训练和优化智能客服系统都是至关重要的。以下是一些通常需要准备的数据&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.对话数…

Android Studio实现内容丰富的安卓民宿酒店预订平台

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看民宿 3.民宿预订 4.民宿预订支付&#xff0c; 5.支付订单 6.评论管…

亚马逊认证考试系列 - 知识点 - Beanstalk简介

Beanstalk是一个开源的分布式消息队列系统&#xff0c;它的设计目标是简单、快速和可靠。Beanstalk使用基于TCP的协议进行通信&#xff0c;支持多种客户端和服务器库&#xff0c;同时还提供了一组命令行工具和RESTful API。 Beanstalk的核心概念包括&#xff1a; Job&#xff…

spring suite搭建springboot操作

一、前言 有时候久了没开新项目了&#xff0c;重新开发一个新项目&#xff0c;搭建springboot的过程都有点淡忘了&#xff0c;所有温故知新。 二、搭建步骤 从0开始搭建springboot 1&#xff0e;创建work空间。步骤FileNewJava Working Set。 2.选择Java Working Set。 3.自…

IPD集成产品开发:塑造企业未来竞争力的关键

随着市场竞争的日益激烈&#xff0c;企业对产品开发的要求也越来越高。如何在快速变化的市场环境中&#xff0c;既保证产品的批量生产效率&#xff0c;又满足客户的个性化需求&#xff0c;成为了企业面临的重要挑战。IPD&#xff08;集成产品开发&#xff09;模式&#xff0c;作…

何为布控球?布控球的分类对比

主要的分类有&#xff1a; 根据内部的主控板卡的系统分类&#xff0c;典型的是基于海思芯片的嵌入式LINUX系统的&#xff0c;一般出国标GB28181&#xff0c;另外一种是剑走偏锋的安卓系统的&#xff0c;需要把球机的输出YUV转换为UVC接入安卓主板&#xff0c;作为外接USB摄像头…

source insight中文乱码怎么办?

很多人不知道source insight中文乱码了该怎么办?今日为你们带来的文章是source insight中文乱码的解决方法&#xff0c;还有不清楚小伙伴和小编一起去学习一下吧。 第一步&#xff1a;首先我们打开source insight软件&#xff0c;进入主界面(如图所示)。 第二步&#xff1a;然…

Python深入理解迭代器和生成器

当说起Python里面的高级特性时&#xff0c;就不能不提迭代器&#xff08;Iterators&#xff09;和生成器&#xff08;Generators&#xff09;啦&#xff01;它们就像是处理数据的一把利器&#xff0c;特别是对付大数据的时候&#xff0c;简直就是神器&#xff01;咱们今天就来聊…

bootstrap表格API文档

表格参数&#xff1a; 名称标签类型默认描述-data-toggleString‘table’不用写 JavaScript 直接启用表格。classesdata-classesString‘table table-hover’表格的类名称。默认情况下&#xff0c;表格是有边框的&#xff0c;你可以添加 ‘table-no-bordered’ 来删除表格的边…

Android 12 SystemUI调试

一、调试步骤 在开发过程中&#xff0c;除了可使用AS(Android Studio)开发工具调试源码&#xff0c;还可使用其他开发工具例如VS code&#xff0c;由于篇幅有限&#xff0c;本文只讲如何使用AS调试System UI&#xff0c;如若需要使用Android Studio进行SystemUI的调试工作&…

QCustomPlot-绘制X轴为日期的折线图

主要代码如下&#xff1a; void Widget::InitQLineXDateAddData() {customPlot new QCustomPlot(this);// 创建日期时间类型的刻度生成器QSharedPointer<QCPAxisTickerDateTime> dateTimeTicker(new QCPAxisTickerDateTime);dateTimeTicker->setDateTimeFormat(&quo…

蓝桥杯第642题——跳蚱蜢

题目描述 如下图所示&#xff1a; 有 9 只盘子&#xff0c;排成 1 个圆圈。 其中 8 只盘子内装着 8 只蚱蜢&#xff0c;有一个是空盘。 我们把这些蚱蜢顺时针编号为 1 ~ 8。 每只蚱蜢都可以跳到相邻的空盘中&#xff0c; 也可以再用点力&#xff0c;越过一个相邻的蚱蜢跳到空盘…

数据库学习记录(一)基础语法与单表查询

基础sql语句分类 DDL操作&#xff08;图形化界面&#xff09; 用来定义数据库对象的&#xff0c;例如创建数据库&#xff0c;创建表单 数据库操作 表操作 DML操作&#xff08;掌握&#xff09; 1、insert为添加语句&#xff0c;该语句功能是添加相关数据到表结构中 下面为添…

【爬虫】 突破Cloudflare 5秒盾的艺术:使用Cloudscraper

无心生大用,有物不通神 &#x1f3b5; 闪现吃血王昭君《道德经》 在当今的互联网世界中&#xff0c;保护网站免受恶意访问变得尤为重要。Cloudflare是一种流行的解决方案&#xff0c;提供了多种安全功能&#xff0c;包括一个被广泛称为"5秒盾"(…

【Anaconda】换源常用命令

【Anaconda】换源常用命令 注意换源只需配置清华或中科大或阿里中的任意一个即可&#xff0c;都列出来只是为了备用&#xff0c;方便查找 每创建一个新的虚拟环境&#xff0c;都需要换一次源 查看当前源 conda config --show channels配置清华源 清华源官网 conda config -…

学习笔记 | 微信小程序项目day04

今日学习内容 热门推荐下转页面 热门推荐下转页面 1、定义类型 import type { PageResult, GoodsItem } from ./global/** 热门推荐 */ export type HotResult {/** id信息 */id: string/** 活动图片 */bannerPicture: string/** 活动标题 */title: string/** 子类选项 */…

tensorflow 随机采样

实现随机采样 dataset tf.data.Dataset.from_tensor_slices((x, y)) dataset dataset.shuffle(len(x)).batch(32)train_size, val_size, test_size 0.8, 0.1, 0.1train_dataset dataset.take(int(train_size * len(dataset))) val_dataset dataset.skip(int(train_size *…