Qt - UDP网络编程 qt tcp udp
bigegpt 2024-12-24 10:52 27 浏览
1. QUdpSocket(通信套接字)
UDP(User Datagram Protocol,用户数据报协议)
UDP是一个轻量级、不可靠、面向数据报的、无连接的协议,多用于可靠性要求不严格,不是非常重要的传输。
QUdpSocket类继承自QAbstractSocket,用来发送和接收UDP数据报,”Socket”即套接字,套接字即IP地址+端口号。其中IP地址指定了网络中的一台主机,二端口号则指定了该主机上的一个网络程序,使用套接字即可实现网络上的两个应用程序之间的通信。
QUdpSocket支持IPv4广播,要广播数据报,则只需发送到一个特殊的地址QHostAddress::Broadcast(即255.255.255.255),数据报一般建议发送字节数小于512字节。端口号选择1024-65535(1024以下的常用作保留端口号,如FTP常用端口号21,Telnet常用端口号23,DNS域名服务器常用端口53等)。
1.1 信号
无
1.2 公有函数
函数 | 描述 |
bool hasPendingDatagrams() const | 如果至少有一个数据报等待读取,则返回true;否则返回false。 |
bool joinMulticastGroup(const QHostAddress &groupAddress) | 加入操作系统选择的默认接口上groupAddress指定的组播组。套接字必须处于BoundState状态,否则会发生错误。 |
bool joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) | 这是一个重载函数。在接口上加入组播组地址groupAddress。 |
bool leaveMulticastGroup(const QHostAddress &groupAddress) | 在操作系统选择的默认接口上离开groupAddress指定的组播组。套接字必须处于BoundState状态,否则会发生错误。 |
bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) | 这是一个重载函数。离开接口上指定groupAddress的组播组。 |
QNetworkInterface multicastInterface() const | 返回多播数据报的出接口。这对应于IPv4套接字的IP_MULTICAST_IF套接字选项和IPv6套接字的IPV6_MULTICAST_IF套接字选项。如果之前没有设置接口,这个函数将返回一个无效的qnetworkinterface。套接字必须处于BoundState状态,否则返回无效的QNetworkInterface。 |
qint64 pendingDatagramSize() const | 返回第一个挂起的UDP数据报的大小。如果没有可用的数据报,这个函数返回-1。 |
qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr) | 接收不大于maxSize字节的数据报,并将其存储在数据中。发送方的主机地址和端口存储在address和port中(除非指针是0)。成功返回数据报的大小;否则返回1。 |
QNetworkDatagram receiveDatagram(qint64 maxSize = -1) | 接收一个不大于maxSize字节的数据报,并在QNetworkDatagram对象中返回它,以及发送者的主机地址和端口。如果可能,该函数还将尝试确定数据报的目的地址、端口和接收时的跳数。 |
void setMulticastInterface(const QNetworkInterface &iface) | 将组播数据报的出接口设置为当前接口。这对应于IPv4套接字的IP_MULTICAST_IF套接字选项和IPv6套接字的IPV6_MULTICAST_IF套接字选项。套接字必须处于BoundState状态,否则此函数不执行任何操作。 |
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port) | 将大小为size的数据报发送到端口上的主机地址。返回成功发送的字节数;否则返回1。 |
qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port) | 这是一个重载函数。将数据报发送到主机地址和端口。 |
?从QAbstractSocket继承了37个公共函数,上面有写QAbstractSocket的公有函数
Qt资料领取→「链接」
1.3 发送与接收函数
QUdpSocket对于发送数据报文提供了三个重载函数:
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)//将大小为size的数据报发送到端口上的主机地址。返回成功发送的字节数;否则返回1。
qint64 writeDatagram(const QNetworkDatagram &datagram)
qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
接收消息需要使用Qudpsocket提供的以下函数:
qint64 pendingDatagramSize() const
qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR)//接收不大于maxSize字节的数据报,并将其存储在数据中。发送方的主机地址和端口存储在address和port中(除非指针是0)。成功返回数据报的大小;否则返回1。
QNetworkDatagram receiveDatagram(qint64 maxSize = -1)
1.4 UDP通信流程
2. UDP消息传送的三种模式
单播模式(unicast):一个UDP客户端发送数据报到指定地址和端口的另一UDP客户端,是一对一的数据传输。
组播模式(multicast):UDP客户端加入到另一个组播IP地址的多播组,成员向组播地址发送的数据报,其加入组播的所有成员都可以接收到,类似于QQ群功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能。
广播模式(broadcast):一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。QUdpSocket支持IPv4广播。需要在数据报中指定接收端地址为QHostAddress::Broadcast,一般的广播地址是255.255.255.255。
在单播、广播和多播模式下,UDP程序都是对等的,不像TCP通信分为客户端和服务端。
TCP通信只有单播模式。UDP通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP通信的。
QUdpSocket也支持UDP组播。 使用joinMulticastGroup()和leaveMulticastGroup()控制组成员,使用QAbstractSocket::MulticastTtlOption和QAbstractSocket::MulticastLoopbackOption设置TTL和loopback套接字选项。 使用setMulticastInterface()控制组播数据报的出接口,使用multicastInterface()查询出接口。
使用QUdpSocket,您还可以使用connectToHost()建立到UDP服务器的虚拟连接,然后使用read()和write()交换数据报,而不需要为每个数据报指定接收者。
Broadcast Sender、Broadcast Receiver、Multicast Sender和Multicast Receiver示例演示了如何在应用程序中使用QUdpSocket。
2.1 单播
单播(Unicast)是在一个单个的发送者和一个接受者之间通过网络进行的通信。
- 发送端:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
class UnicastSender:public QWidget
{
Q_OBJECT
public:
UnicastSender(QWidget*parent = nullptr)
{
resize(300,100);
statusLab = new QLabel("没有正在发送的数据报");
sendBtn = new QPushButton("发送数据");
auto vlayout = new QVBoxLayout(this);
vlayout->addWidget(statusLab);
vlayout->addWidget(sendBtn);
initSocket();
connect(sendBtn,&QPushButton::released,this,&UnicastSender::sendDatagram);
}
void initSocket()
{
udpSoket = new QUdpSocket(this);
//不需要连接到服务器
}
void sendDatagram()
{
QByteArray datagram = "数据报:"+QByteArray::number(messageNo);
//发送数据,需要指定ip地址和端口号
udpSoket->writeDatagram(datagram,QHostAddress::LocalHost /*QHostAddress("81.70.201.21")*/,8888);
messageNo++;
}
private:
QLabel * statusLab;
QPushButton* sendBtn;
int messageNo = 0;
QUdpSocket *udpSoket;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
UnicastSender sender;
sender.show();
return a.exec();
}
//包含moc文件
#include"main.moc"
- 接受端:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
class UnicastRecevier:public QWidget
{
Q_OBJECT
public:
UnicastRecevier(QWidget*parent = nullptr)
{
resize(300,100);
statusLab = new QLabel("没有接受到数据");
auto vlayout = new QVBoxLayout(this);
vlayout->addWidget(statusLab);
initSocket();
}
void initSocket()
{
udpSoket = new QUdpSocket(this);
//接收者需要绑定ip地址和端口号
udpSoket->bind(QHostAddress::LocalHost/*QHostAddress("81.70.201.21")*/,8888);
connect(udpSoket,&QUdpSocket::readyRead,this,&UnicastRecevier::onReadyread);
}
void onReadyread()
{
QByteArray datagram;
QHostAddress host;
quint16 port;
//读取数据
while(udpSoket->hasPendingDatagrams())
{
//获取一下下一个数据报的大小
datagram.resize(udpSoket->pendingDatagramSize());
//接受
udpSoket->readDatagram(datagram.data(),datagram.size(),&host,&port);
//显示
statusLab->setText(host.toString() +":"+ QString::number(port) +datagram);
}
}
private:
QLabel * statusLab;
int messageNo = 0;
QUdpSocket *udpSoket;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
UnicastRecevier sender;
sender.show();
return a.exec();
}
//包含moc文件
#include"main.moc"
2.2 多播
多播(Multicast)是一点对多点的通信,IPv6没有采用IPv4中的组播术语,而是将广播看成是多播的一个特殊例子。
多播与单播步骤是一样的,只有IP地址有所区别。
多播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:
- 1,局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包
- 2,预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。
- 3,管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。
多播的程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的,其选项值和含义参见11.5所示。
选项 | 描述 |
IP_MULTICAST_TTL | 设置多播组数据的TTL值 |
IP_ADD_MEMBERSHIP | 在指定接口上加入组播组 |
IP_DROP_MEMBERSHIP | 退出组播组 |
IP_MULTICAST_IF | 获取默认接口或设置接口 |
IP_MULTICAST_LOOP | 禁止组播数据回送 |
多播程序设计的框架
要进行多播的编程,需要遵从一定的编程框架。多播程序框架主要包含套接字初始化、设置多播超时时间、加入多播组、发送数据、接收数据以及从多播组中离开几个方面。其步骤如下:
(1)建立一个socket。
(2)然后设置多播的参数,例如超时时间TTL、本地回环许可LOOP等。
(3)加入多播组。
(4)发送和接收数据。
(5)从多播组离开。
- 发送端:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
class MulticastSender:public QWidget
{
Q_OBJECT
public:
MulticastSender(QWidget*parent = nullptr)
{
resize(300,100);
statusLab = new QLabel("没有正在发送的数据报");
sendBtn = new QPushButton("发送数据");
auto vlayout = new QVBoxLayout(this);
vlayout->addWidget(statusLab);
vlayout->addWidget(sendBtn);
initSocket();
connect(sendBtn,&QPushButton::released,this,&MulticastSender::sendDatagram);
}
void initSocket()
{
udpSoket = new QUdpSocket(this);
//不需要连接到服务器
}
void sendDatagram()
{
QByteArray datagram = "数据报:"+QByteArray::number(messageNo);
//发送数据,需要指定ip地址和端口号,组播ip地址范围:224.0.0.0 - 239.255.255.255
udpSoket->writeDatagram(datagram,QHostAddress("239.255.255.255"),8887);
messageNo++;
}
private:
QLabel * statusLab;
QPushButton* sendBtn;
int messageNo = 0;
QUdpSocket *udpSoket;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MulticastSender sender;
sender.show();
return a.exec();
}
//包含moc文件
#include"main.moc"
- 接受端:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
class MulticastRecevier:public QWidget
{
Q_OBJECT
public:
MulticastRecevier(QWidget*parent = nullptr)
{
resize(300,100);
statusLab = new QLabel("没有接受到数据");
auto vlayout = new QVBoxLayout(this);
vlayout->addWidget(statusLab);
initSocket();
}
void initSocket()
{
udpSoket = new QUdpSocket(this);
//接收者需要绑定ip地址和端口号,允许端口和ip重用,多个socket绑定同一ip和端口
udpSoket->bind(QHostAddress::AnyIPv4,8887,QUdpSocket::BindFlag::ShareAddress | QUdpSocket::BindFlag::ReuseAddressHint);
//多播是分组的,加入一个指定组
udpSoket->joinMulticastGroup(QHostAddress("239.255.255.255"));
connect(udpSoket,&QUdpSocket::readyRead,this,&MulticastRecevier::onReadyread);
}
void onReadyread()
{
QByteArray datagram;
QHostAddress host;
quint16 port;
//读取数据
while(udpSoket->hasPendingDatagrams())//hasPendingDatagrams()如果至少有一个数据报等待读取,则返回true;
{
//获取一下下一个数据报的大小
datagram.resize(udpSoket->pendingDatagramSize());//pendingDatagramSize()返回第一个挂起的UDP数据报的大小。
//接受
udpSoket->readDatagram(datagram.data(),datagram.size(),&host,&port);
//显示
statusLab->setText(host.toString() +":"+ QString::number(port) +datagram);
}
}
private:
QLabel * statusLab;
int messageNo = 0;
QUdpSocket *udpSoket;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MulticastRecevier sender;
sender.show();
return a.exec();
}
//包含moc文件
#include"main.moc"
测试效果:
2.3 广播
广播(broadcast)是一点到所有点的通信方式。
广播与组播是一样的,只是ip地址有所不同,而且不用加入指定的组。单播的数据只是收发数据的特定主机进行处理,组播在特定组之间进行处理,而广播的数据整个局域网都进行处理。
“广播”可以理解为一个人通过广播喇叭对在场的全体说话,这样做的好处是通话效率高,信息一下子就可以传递到全体。
“广播”在网络中的应用较多,如客户机通过DHCP自动获得IP地址的过程就是通过广播来实现的。但是同单播和多播相比,广播几乎占用了子网内网络的所有带宽。拿开会打一个比方吧,在会场上只能有一个人发言,想象一下如果所有的人同时都用麦克风发言,那会场上就会乱成一锅粥。
在IP网络中,广播地址用IP地址“255.255.255.255”来表示,这个IP地址代表同一子网内所有的IP地址。
- 发送端:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
class BroadcastSender:public QWidget
{
Q_OBJECT
public:
BroadcastSender(QWidget*parent = nullptr)
{
resize(300,100);
statusLab = new QLabel("没有正在发送的数据报");
sendBtn = new QPushButton("发送数据");
auto vlayout = new QVBoxLayout(this);
vlayout->addWidget(statusLab);
vlayout->addWidget(sendBtn);
initSocket();
connect(sendBtn,&QPushButton::released,this,&BroadcastSender::sendDatagram);
}
void initSocket()
{
udpSoket = new QUdpSocket(this);
//不需要连接到服务器
}
void sendDatagram()
{
QByteArray datagram = "数据报:"+QByteArray::number(messageNo);
//发送数据,需要指定ip地址和端口号
udpSoket->writeDatagram(datagram,QHostAddress::Broadcast,8886);
messageNo++;
}
private:
QLabel * statusLab;
QPushButton* sendBtn;
int messageNo = 0;
QUdpSocket *udpSoket;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
BroadcastSender sender;
sender.show();
return a.exec();
}
//包含moc文件
#include"main.moc"
- 接受端:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
class BroadcastRecevier:public QWidget
{
Q_OBJECT
public:
BroadcastRecevier(QWidget*parent = nullptr)
{
resize(300,100);
statusLab = new QLabel("没有接受到数据");
auto vlayout = new QVBoxLayout(this);
vlayout->addWidget(statusLab);
initSocket();
}
void initSocket()
{
udpSoket = new QUdpSocket(this);
//接收者需要绑定ip地址和端口号
udpSoket->bind(QHostAddress::Any,8886,QUdpSocket::BindFlag::ShareAddress | QUdpSocket::BindFlag::ReuseAddressHint);
connect(udpSoket,&QUdpSocket::readyRead,this,&BroadcastRecevier::onReadyread);
}
void onReadyread()
{
QByteArray datagram;
QHostAddress host;
quint16 port;
//读取数据
while(udpSoket->hasPendingDatagrams())//hasPendingDatagrams()如果至少有一个数据报等待读取,则返回true;
{
//获取一下下一个数据报的大小
datagram.resize(udpSoket->pendingDatagramSize());//pendingDatagramSize()返回第一个挂起的UDP数据报的大小。
//接受
udpSoket->readDatagram(datagram.data(),datagram.size(),&host,&port);
//显示
statusLab->setText(host.toString() +":"+ QString::number(port) +datagram);
}
}
private:
QLabel * statusLab;
int messageNo = 0;
QUdpSocket *udpSoket;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
BroadcastRecevier sender;
sender.show();
return a.exec();
}
//包含moc文件
#include"main.moc"
测试效果:
文章转自博客园(BORUTO):https://www.cnblogs.com/zhuchunlin/p/16485948.html
Qt资料领取→「链接」
相关推荐
- 得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践
-
一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...
- warm-flow新春版:网关直连和流程图重构
-
本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...
- 扣子空间体验报告
-
在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...
- spider-flow:开源的可视化方式定义爬虫方案
-
spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...
- solon-flow 你好世界!
-
solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...
- 新一代开源爬虫平台:SpiderFlow
-
SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...
- 通过 SQL 训练机器学习模型的引擎
-
关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...
- 鼠须管输入法rime for Mac
-
鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...
- Go语言 1.20 版本正式发布:新版详细介绍
-
Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...
- iOS 10平台SpriteKit新特性之Tile Maps(上)
-
简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...
- 程序员简历例句—范例Java、Python、C++模板
-
个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...
- Telerik UI for iOS Q3 2015正式发布
-
近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...
- ios使用ijkplayer+nginx进行视频直播
-
上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...
- IOS技术分享|iOS快速生成开发文档(一)
-
前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...
- macOS下配置VS Code C++开发环境
-
本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- httperror403.14-forbidden (63)
- logstashinput (65)
- hadoop端口 (65)
- dockernetworkconnect (63)
- esxi7 (63)
- vue阻止冒泡 (67)
- c#for循环 (63)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- java大写转小写 (63)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)