【QT】UDP

news2024/11/27 19:55:17

目录

核心API

示例:回显服务器 

服务器端编写:

第一步:创建出socket对象

第二步: 连接信号槽

 第三步:绑定端口号

 第四步:编写信号槽所绑定方法

 第五步:编写第四步中处理请求的方法

客户端编写:

界面设计

代码编写

发送按钮槽函数 

处理响应函数

完整代码:

测试结果:


注意:

        使用Qt网络编程的API,需要先在 .pro文件中添加 network模块! !

QT       += core gui network

核心API

主要的类有两个  QUdpSocket 和 QNetworkDatagram,upd传输的是数据报,QNetworkDatagram就是qt中udp传输的内容;

QUdpSocket 表示⼀个 UDP 的 socket ⽂件.

名称类型说明对应原生API
bind(const QHostAddress&, quint16)
方法
绑定指定的端⼝号.
bind
receiveDatagram()
方法
返回QNetworkDatagram读取⼀个 UDP 数据报
recvfrom
writeDatagram(const QNetworkDatagram&)
方法
发送⼀个 UDP 数据报
sendto
readyRead
信号
在收到数据并准备就绪后触发
⽆ (类似于 IO 多路复⽤的通 知机制)
QNetworkDatagram 表示⼀个 UDP 数据报
名称类型说明对应原生API
QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 )
构造函
通过 QByteArray , ⽬标 IP 地址, ⽬标端⼝号 构造⼀个 UDP 数据报. 通常⽤于发送数据时.
data()
方法
获取数据报内部持有的数据. 返回
QByteArray
senderAddress()
方法
获取数据报中包含的对端的 IP 地
址.
senderPort()
方法
获取数据报中包含的对端的端⼝号.
;无

在编写udp相关代码时,注意事项:

  •  一定要先连接信号槽,再绑定端口号,一旦绑定端口了,意味着请求就可以被收到了,如果在完成绑定之后,在连接信号槽之前,有客户端把请求发过来了,此时就可能读不到这样的请求。
  •  一个端口号只能被一个socket绑定。 
  •  socket->errorString() 本质上也是对系统的errno机制进行封装

示例:回显服务器 

服务器端编写:

第一步:创建出socket对象

socket = new QUdpSocket(this);

第二步: 连接信号槽

connect(socket , &QUdpSocket::readyRead , this , &Widget::processRequest);

 第三步:绑定端口号

这里记得判断是否绑定成功,如果端口号被其他socket所绑定,我们就不能在绑定该端口号!

    bool ret = socket->bind(QHostAddress::Any,9090);

    if(!ret)

    {

        //绑定失败

        QMessageBox::critical(this,"服务器启动出错",socket->errorString());

        return;

    }

 第四步:编写信号槽所绑定方法

void Widget::processRequest()

{

    //1.读取请求并解析

    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();

    QString request = requestDatagram.data(); //这里data()返回的是QByte数据

    //2.根据请求计算响应(由于这里仅仅是回显服务器,响应不需要计算,就是请求本身)

    const QString& response = process(request);

    //3.把响应写回客户端,响应数据包(数据是啥,客户端ip地址,客户端端口)

    QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(),requestDatagram.senderPort());

    //response.toUtf8() 取出QString内部的字节数组

    socket->writeDatagram(responseDatagram);

    //4.把这次交互的信息,显示到界面上

    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())+

            "] req:" + request + ", resp:" + response;

    ui->listWidget->addItem(log);

}

 第五步:编写第四步中处理请求的方法

QString Widget::process(const QString &request)

{

    //请求处理过程,由于当前是回显服务i,响应和请求完全一样,不需要进行处理

    return request;

}

完整代码如下: 

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //创建出这个对象
    socket = new QUdpSocket(this);

    //设置窗口标题
    this->setWindowTitle("服务器");

    //连接信号槽
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processRequest);

    //绑定端口号
    bool ret = socket->bind(QHostAddress::Any,9090);
    if(!ret)
    {
        //绑定失败
        QMessageBox::critical(this,"服务器启动出错",socket->errorString());
        return;
    }

}

Widget::~Widget()
{
    delete ui;
}

void Widget::processRequest()
{
    //1.读取请求并解析
    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
    QString request = requestDatagram.data();

    //2.根据请求计算响应(由于这里仅仅是回显服务器,响应不需要计算,就是请求本身)
    const QString& response = process(request);

    //3.把响应写回客户端,响应数据包(数据是啥,客户端ip地址,客户端端口)
    QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(),requestDatagram.senderPort());
    //response.toUtf8() 取出QString内部的字节数组
    socket->writeDatagram(responseDatagram);
    //4.把这次交互的信息,显示到界面上
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())+
            "] req:" + request + ", resp:" + response;

    ui->listWidget->addItem(log);
}

QString Widget::process(const QString &request)
{
    //请求处理过程,由于当前是回显服务i,响应和请求完全一样,不需要进行处理
    return request;
}




客户端编写:

界面设计

我们客户端简单设计成如下:

效果如下:

端口号本质上是一个2字节的无符号整数  。

代码编写

首先我们写定义两个常量,描述服务器的地址和端口:

const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;

发送按钮槽函数 

void Widget::on_pushButton_clicked()

{

    //1.获取输入框的内容

    const QString& text = ui->lineEdit->text();

    //2. 构造UDP请求数据,这里ip我们是QString,参数识别不出来,需要我们进行转换

    QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);

    //3. 发送请求数据

    socket->writeDatagram(requestDatagram);

    //4. 把发送的请求数据也添加到列表框中

    ui->listWidget->addItem("客户端说:" + text);

    //5. 把输入框的内容也清空一下,方便下次输入

    ui->lineEdit->setText("");

}

处理响应函数

void Widget::processHandle()

{

    //通过这个函数来处理收到的响应

    //1.读取到响应的数据

    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();

    //这里不用换引用是因为.data()返回的是QByte类型,涉及类型转换

    QString response =responseDatagram.data();

    //2. 把响应数据显示到界面上

    ui->listWidget->addItem("服务器说:"+response);

}

完整代码:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void processHandle();
private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket* socket;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QNetworkDatagram>
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket = new QUdpSocket(this);

    //修改窗口标题,区分这是一个客户端程序
    this->setWindowTitle("客户端");

    //通过信号槽,来处理服务器返回的数据
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processHandle);

}
Widget::~Widget()
{
    delete ui;
}


void Widget::processHandle()
{
    //通过这个函数来处理收到的响应
    //1.读取到响应的数据
    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
    QString response =responseDatagram.data();
    //2. 把响应数据显示到界面上
    ui->listWidget->addItem("服务器说:"+response);
}

void Widget::on_pushButton_clicked()
{
    //1.获取输入框的内容
    const QString& text = ui->lineEdit->text();
    //2. 构造UDP请求数据
    QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);
    //3. 发送请求数据
    socket->writeDatagram(requestDatagram);
    //4. 把发送的请求数据也添加到列表框中
    ui->listWidget->addItem("客户端说:" + text);
    //5. 把输入框的内容也清空一下,方便下次输入
    ui->lineEdit->setText("");
}

测试结果:

要想开启多个客户端,我们只需要找到项目对应的文件中,运行.exe文件即可 

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

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

相关文章

Simulink代码生成: 基本模块的使用

文章目录 1 引言2 模块使用实例2.1 In/Out模块2.2 Constant模块2.3 Scope/Display模块2.4 Ground/Terminator模块 3 总结 1 引言 本文中博主介绍Simulink中最简单最基础的模块&#xff0c;包括In/Out模块&#xff08;输入输出&#xff09;&#xff0c;Constant模块&#xff08…

Postman测试工具详细解读

目录 一、Postman的基本概念二、Postman的主要功能1. 请求构建2. 响应查看3. 断言与自动化测试4. 环境与变量5. 集合与文档化6. 与团队实时协作 三、Postman在API测试中的重要性1. 提高测试效率2. 保障API的稳定性3. 促进团队协作4. 生成文档与交流工具 四、Postman的使用技巧1…

CAS算法

CAS算法 1. CAS简介 CAS叫做CompareAndSwap&#xff0c;比较并交换&#xff0c;主要是通过处理器的指令来保证操作的原子性。 CAS基本概念 内存位置 (V)&#xff1a;需要进行CAS操作的内存地址。预期原值 (A)&#xff1a;期望该内存位置上的旧值。新值 (B)&#xff1a;如果旧…

VSCode python autopep8 格式化 长度设置

ctrl, 打开设置 > 搜索autopep8 > 找到Autopep8:Args > 添加项--max-line-length150

Java泛型的介绍和基本使用

什么是泛型 ​ 泛型就是将类型参数化&#xff0c;比如定义了一个栈&#xff0c;你必须在定义之前声明这个栈中存放的数据的类型&#xff0c;是int也好是double或者其他的引用数据类型也好&#xff0c;定义好了之后这个栈就无法用来存放其他类型的数据。如果这时候我们想要使用这…

谷粒商城实战笔记-71-商品服务-API-属性分组-前端组件抽取父子组件交互

文章目录 一&#xff0c;一次性创建所有的菜单二&#xff0c;开发属性分组界面1&#xff0c;左侧三级分类树形组件2&#xff0c;右侧分组列表3&#xff0c;左右两部分通信3.1 子组件发送数据3.2&#xff0c;父组件接收数据 Vue的父子组件通信父组件向子组件传递数据子组件向父组…

SpringBoot添加密码安全配置以及Jwt配置

Maven仓库&#xff08;依赖查找&#xff09; 1、SpringBoot安全访问配置 首先添加依赖 spring-boot-starter-security 然后之后每次启动项目之后&#xff0c;访问任何的请求都会要求输入密码才能请求。&#xff08;如下&#xff09; 在没有配置的情况下&#xff0c;默认用户…

LLM agentic模式之工具使用: Gorilla

Gorilla Gorilla出自2023年5月的论文《Gorilla: Large Language Model Connected with Massive APIs》&#xff0c;针对LLM无法准确地生成API调用时的参数&#xff0c;构建API使用数据集后基于Llama微调了一个模型。 数据集构建 API数据集APIBench的构建过程如下&#xff1…

《Programming from the Ground Up》阅读笔记:p75-p87

《Programming from the Ground Up》学习第4天&#xff0c;p75-p87总结&#xff0c;总计13页。 一、技术总结 1.persistent data p75, Data which is stored in files is called persistent data, because it persists in files that remain on disk even when the program …

C语言程序设计15

程序设计15 问题15_1代码15_1结果15_1 问题15_2代码15_2结果15_2 问题15_3代码15_3结果15_3 问题15_1 在 m a i n main main 函数中将多次调用 f u n fun fun 函数&#xff0c;每调用一次&#xff0c;输出链表尾部结点中的数据&#xff0c;并释放该结点&#xff0c;使链表缩短…

【SQL 新手教程 3/20】关系模型 -- 外键

&#x1f497; 关系数据库建立在关系模型上⭐ 关系模型本质上就是若干个存储数据的二维表 记录 (Record)&#xff1a; 表的每一行称为记录&#xff08;Record&#xff09;&#xff0c;记录是一个逻辑意义上的数据 字段 (Column)&#xff1a;表的每一列称为字段&#xff08;Colu…

Buildroot 构建 Linux 系统

Buildroot 是一个工具&#xff0c;以简化和自动化为嵌入式系统构建完整 Linux 系统的过程。使用交叉编译技术&#xff0c;Buildroot 能够生成交叉编译工具链、根文件系统、Linux 内核映像和针对目标设备的引导加载程序。可以独立地使用这些选项的任何组合&#xff0c;例如&…

Vitis AI 使用 VAI_Q_PYTORCH 工具

目录 1. 简介 2. 资料汇总 3. 示例解释 3.1 快速上手示例 4. 总结 1. 简介 vai_q_pytorch 是 Vitis AI Quantizer for Pytorch 的缩写&#xff0c;主要作用是优化神经网络模型。它是 Vitis AI 平台的一部分&#xff0c;专注于神经网络的深度压缩。 vai_q_pytorch 的作用…

大数据管理中心设计规划方案(可编辑的43页PPT)

引言&#xff1a;随着企业业务的快速发展&#xff0c;数据量急剧增长&#xff0c;传统数据管理方式已无法满足高效处理和分析大数据的需求。建立一个集数据存储、处理、分析、可视化于一体的大数据管理中心&#xff0c;提升数据处理能力&#xff0c;加速业务决策过程&#xff0…

Spring Boot:图书管理系统(一)

1.编写用户登录接口 代码&#xff1a; package com.example.demo;import jakarta.servlet.http.HttpSession; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotatio…

HarmonyOS和OpenHarmony区别联系

前言 相信我们在刚开始接触鸿蒙开发的时候经常看到HarmonyOS和OpenHarmony频繁的出现在文章和文档之中&#xff0c;那么这两个名词分别是什么意思&#xff0c;他们之间又有什么联系呢&#xff1f;本文将通过现有的文章和网站内容并与Google的AOSP和Android做对比&#xff0c;带…

Vue.js 2 项目实战(五):水果购物车

前言 Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架。它的设计目标是通过采用易于上手的结构和强大的功能&#xff0c;使前端开发变得更加简便和高效。以下是 Vue.js 的一些关键特性和优点&#xff1a; 核心特性 声明式渲染 Vue.js 使用声明式语法来描述用户界面&a…

AI周报(7.21-7.27)

AI应用-一款能提供情绪价值的智能鸟类喂食器&#xff08;Bird Buddy&#xff09; 图像识别技术&#xff1a;Bird Buddy装备了图像识别技术&#xff0c;能够识别超过1000种鸟类&#xff0c;涵盖了常见的鸟类品种。这种技术能够在鸟类经过时&#xff0c;通过内置麦克风捕捉的声音…

PID 控制实验 - 整定实验

Arduino PID Arduino-PID-LibraryArduino-PID-AutoTune-Library PID控制实验 – 制作测试台 PID Control Experiment – Making the Testing Rig PID (Proportional, Integral, Derivative) control is a classic control algorithm that I have used for a few projects,…

Debug-018-elementUI-el-tree中通过CSS隐藏任意一项的选择框checkbox

前情提要&#xff1a; 我们项目中使用的是elementUI&#xff0c;业务中经常需要使用到el-tree组件去实现一些有层级关系的功能。现在有一个需求描述一下&#xff1a;首先是这个el-tree是个有checkbox的树&#xff0c;每一子节点都可以被选择&#xff0c;用于去实现一些系统的权…