Qt学习(十五)

您所在的位置:网站首页 滤波器概念龙头股 Qt学习(十五)

Qt学习(十五)

2024-07-17 17:31:33| 来源: 网络整理| 查看: 265

在这里插入图片描述

UDP通信是无连接的,与TCP通信相比,少了一步建立连接的过程,只要经过绑定,就可以直接进行数据的发送和接收。

在Qt的UDP通信中,由于少了连接这一个步骤,客户端和服务端没有太大区别,所以也可以看作只有发送端和接收端。无论是发送端还是接收端,都只有一个套接字,也就是QUdpSocket。此外,UDP通信中没有监听listen(),只有绑定bind(),往套接字中读写数据用的是readDatagram()和writeDatafram(),关闭套接字时同样是调用close()。

P.S:datagram是数据报 / 数据包 / 数据报文的意思

和TCP相同的地方是,发送端向接收端发送数据时,会触发接收端的readyRead()信号。

UDP收发消息的实现

首先,为了方便测试需要绘制两个窗口,在它们之间互相通信:

//WidgetA.h #ifndef WIDGETA_H #define WIDGETA_H #include #include #include #include class WidgetA : public QWidget { Q_OBJECT public: WidgetA(QWidget *parent = 0); ~WidgetA(); private: QLineEdit *p_ipEdit; QLineEdit *p_portEdit; QPushButton *p_connectButton; QTextEdit *p_sendBox; QPushButton *p_sendButton; QPushButton *p_closeButton; }; #endif // WIDGETA_H 复制代码 //WidgetA.cpp #include "WidgetA.h" #include WidgetA::WidgetA(QWidget *parent) : QWidget(parent) { this->resize(640,480); this->move(300,300); this->setWindowTitle("Port 9999"); QGridLayout *p_layout=new QGridLayout(this); p_ipEdit=new QLineEdit(this); p_portEdit=new QLineEdit(this); p_sendBox=new QTextEdit(this); p_sendButton=new QPushButton("send",this); p_closeButton=new QPushButton("close",this); p_layout->addWidget(p_ipEdit,0,0,1,10); p_layout->addWidget(p_portEdit,1,0,1,10); p_layout->addWidget(p_sendBox,2,0,8,10); p_layout->addWidget(p_sendButton,10,0,1,2); p_layout->addWidget(p_closeButton,10,9,1,1); } WidgetA::~WidgetA() { } 复制代码

另一个窗口可以增加一个C++文件,用相同的代码实现,也可以直接new一个WidgetA对象,在main.cpp中为不同的对象绑定不同的端口。

//main.cpp #include "WidgetA.h" #include "WidgetB.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); WidgetA wa; WidgetB wb; wa.show(); wb.show(); return a.exec(); } 复制代码

实现效果: 在这里插入图片描述 接下来实现消息的收发功能,在开始之前,记得在.pro文件中加上QT += network,然后进行构建。

bind()

接着,创建通信套接字QUdpSocket:

//绑定端口 p_udpSocket=new QUdpSocket(this); p_udpSocket->bind(9999); 复制代码 readDatagram()

当接收方接收到数据时,会触发readyRead()信号,在与之对应的槽函数中,我们需要调用readDatagram()方法来获取发送方发送过来的数据报内容以及发送方的主机地址和端口号。

P.S: readDatagram()方法的有四个参数: 第一个参数是存储数据报内容的char型数组; 第二个参数是数据报的最大长度,超过这个长度的数据会丢失; 第三个参数是发送者的主机地址,类型为QHostAddress; 第四个参数是发送者的端口号,类型为qint16。 这个方法的返回值是成功读取的字符数。

void WidgetB::readData(){ //获取发送者的IP和端口号以及数据报内容 char array[1024]; //用于接收数据报内容的char型数据 QHostAddress m_ip; //用于接收发送方IP地址的变量 quint16 m_port; //用于接收发送方端口号的变量,注意是quint16类型 qint64 m_len=p_udpSocket->readDatagram(array,sizeof(array),&m_ip,&m_port); //组包 if(m_len>0){ QString str=QString("[%1:%2]:%3").arg(m_ip.toString()).arg(m_port).arg(array); //设置文本区内容 p_sendBox->setText(str); } } 复制代码

P.S:端口号这里的类型要用quint16而不是qint16,否则会报错。

writeDatagram()

当作为发送方发送数据时,需要从窗口中获取目标主机地址和端口号,然后调用writeDatagram()将数据写到通信套接字中:

void WidgetB::sendData(){ //获取接收方的IP端口号 QString m_ip=p_ipEdit->text(); quint16 m_port=p_portEdit->text().toInt(); //获取文本区内 QString str=p_sendBox->toPlainText(); //往通信套接字中写数据 p_udpSocket->writeDatagram(str.toUtf8(),QHostAddress(m_ip),m_port); } 复制代码

P.S:端口号的类型是quint16,而主机地址的类型是QHostAddress。

实现效果: 在这里插入图片描述

close()

若想关闭UDP套接字,直接调用close()即可:

void WidgetB::closeConnection(){ p_udpSocket->close(); } 复制代码

实现效果: 在这里插入图片描述 结论:当A关闭通信套接字时,它依然可以向B发送消息,B在没有关闭通信套接字的情况下也依然可以接收到A发送过来的消息,但A接收不到B发送过来的消息。

UDP广播和组播 UDP广播

UDP进行广播时,同一个局域网中的所有主机都能接收到数据报。但哪些应用程序会收到消息取决于端口号。

UDP的广播地址为255.255.255.255。

实现效果: 在这里插入图片描述

虽然IP设置为255.255.255.255,但IP地址为127.0.0.1且端口号为8888的通信套接字还是收到了消息。当然,同一局域网下其他主机端口号为8888的进程也能收到消息。

UDP组播

我们在使用广播发送消息的时候会发送给所有用户,但是有些用户是不想接受消息的,这时候我们就应该使用组播,接收方只有先注册到组播地址中才能收到组播消息,否则则接受不到消息。

UDP中的组播地址必须是D类地址。D类地址有:

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为本地管理组播地址,仅在特定的本地范围内有效。

可以调用joinMultiGroup()方法来加入一个组播。但是加入组播之后,bind()中就不能再使用默认的主机地址了,必须指定QHostAddress,否则会有如下警告: 在这里插入图片描述 可供选择的QHostAddress有:

QHostAddress::Null QHostAddress::LocalHost QHostAddress::LocalHostIPv6 QHostAddress::Broadcast QHostAddress::AnyIPv4 QHostAddress::AnyIPv6 QHostAddress::Any p_udpSocket->bind(QHostAddress::AnyIPv4,8888); p_udpSocket->joinMulticastGroup(QHostAddress("244.0.0.2")); 复制代码

实现效果: 在这里插入图片描述 P.S: 接收到的数据在末尾多了一个“1”,是因为我们准备用来接收数据的数组array没有被数据填满,也没有遇到结束符,因此会产生意想不到的结果,比如数据是中文时,会出现乱码。 要解决这个问题,可以利用获取到的数据长度m_len,置array[m_len]=’\0’。

qint64 m_len=p_udpSocket->readDatagram(array,sizeof(array),&m_ip,&m_port); //获取成功读取的字符数 array[m_len]='\0'; //加入结束符 复制代码

也可以使用pendingDatagramSize()方法来获取数据报的大小,根据返回值来准备对应大小的内存空间存放将要接收的数据。

完整代码:

//WidgetA.h #ifndef WIDGETA_H #define WIDGETA_H #include #include #include #include #include class WidgetA : public QWidget { Q_OBJECT public: WidgetA(QWidget *parent = 0); ~WidgetA(); private: QLineEdit *p_ipEdit; QLineEdit *p_portEdit; QPushButton *p_connectButton; QTextEdit *p_sendBox; QPushButton *p_sendButton; QPushButton *p_closeButton; QUdpSocket *p_udpSocket; protected: void WidgetA::readData(); void WidgetA::sendData(); void WidgetA::closeConnection(); }; #endif // WIDGETA_H 复制代码 //WidgetA.cpp #include "WidgetA.h" #include #include WidgetA::WidgetA(QWidget *parent) : QWidget(parent) { //绘制界面 this->resize(640,480); this->move(200,300); this->setWindowTitle("Port 9999"); QGridLayout *p_layout=new QGridLayout(this); p_ipEdit=new QLineEdit(this); p_portEdit=new QLineEdit(this); p_sendBox=new QTextEdit(this); p_sendButton=new QPushButton("send",this); p_closeButton=new QPushButton("close",this); p_layout->addWidget(p_ipEdit,0,0,1,10); p_layout->addWidget(p_portEdit,1,0,1,10); p_layout->addWidget(p_sendBox,2,0,8,10); p_layout->addWidget(p_sendButton,10,0,1,2); p_layout->addWidget(p_closeButton,10,9,1,1); //绑定端口 p_udpSocket=new QUdpSocket(this); p_udpSocket->bind(QHostAddress::AnyIPv4,9999); p_udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2")); connect(p_udpSocket,&QUdpSocket::readyRead,this,&WidgetA::readData); connect(p_sendButton,&QPushButton::clicked,this,&WidgetA::sendData); connect(p_closeButton,&QPushButton::clicked,this,&WidgetA::closeConnection); } WidgetA::~WidgetA() { } void WidgetA::readData(){ //获取发送者的IP和端口号以及数据报内容 char array[1024]; QHostAddress m_ip; quint16 m_port; qint64 m_len=p_udpSocket->readDatagram(array,sizeof(array),&m_ip,&m_port); //组包 if(m_len>0){ QString str=QString("[%1:%2]:%3").arg(m_ip.toString()).arg(m_port).arg(array); array[m_len]='\0'; //设置文本区内容 p_sendBox->append(str); } } void WidgetA::sendData(){ //获取接收方的IP端口号 QString m_ip=p_ipEdit->text(); quint16 m_port=p_portEdit->text().toInt(); //获取文本区内 QString str=p_sendBox->toPlainText(); //往通信套接字中写数据 p_udpSocket->writeDatagram(str.toUtf8(),QHostAddress(m_ip),m_port); } void WidgetA::closeConnection(){ p_udpSocket->close(); } 复制代码

另外两个窗口实现的方法类似。

//main.cpp #include "WidgetA.h" #include "WidgetB.h" #include "WidgetC.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); WidgetA wa; WidgetB wb; WidgetC wc; wa.show(); wb.show(); wc.show(); return a.exec(); } 复制代码

实现效果: 在这里插入图片描述 如果想要退出组播,调用通信套接字的leaveMultiGroup()方法即可。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


    图片新闻

    实验室药品柜的特性有哪些
    实验室药品柜是实验室家具的重要组成部分之一,主要
    小学科学实验中有哪些教学
    计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
    实验室各种仪器原理动图讲
    1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
    高中化学常见仪器及实验装
    1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
    微生物操作主要设备和器具
    今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
    浅谈通风柜使用基本常识
     众所周知,通风柜功能中最主要的就是排气功能。在

    专题文章

      CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭