当前位置: 首页 > article >正文

Qt网络编程——QUdpSocket

文章目录

    • Qt网络编程
    • QUdpSocket
      • Udp回显服务器
      • Udp客户端

Qt网络编程

网络编程本质上是写应用层代码,需要传输层提供支持。

而传输层最核心的协议就是UDP和TCP,这两个协议有较大差别,所以Qt提供了两套API。

要是有Qt网络编程的API,需要现在.pro文件当中添加network模块。

之前的各种控件,各种内容都是包含在QtCore模块当中的(默认添加)

为什么要划分模块?

Qt本身十分庞大,包含了很多框架,如果把所有的Qt功能放到一起,即是写一个简单的hello world,此时生成的可执行程序也会非常庞大。

比如说,Qt会应用到嵌入式,嵌入式系统的配置并没有那么高,所以对空间的利用 > 对时间的利用

进行模块化处理,在默认情况下,其他额外的模块不参与编译,用的时候在.pro文件中引入对应的模块,才能把对应功能给编译加载进来。

网络编程是操作系统提供的API,

C++标准库里面还未提供网络编程的api封装

QUdpSocket

名称类型说明原生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地址无,recvfrom包含此类功能
senderPort()方法获取数据报中包含的对端的端口号无,recvfrom包含此类功能

Udp回显服务器

一般服务器都没有图形化界面,此处为了更加直观看到,采用图形化界面方式

ui界面:

image-20240924212556093

.pro引入network模块

image-20240924213217801

widget.h

这里需要包含QUdpSocket头文件,包含之后可能还是会显示报错,将程序重新编译运行一下即可

#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();

private:
    Ui::Widget *ui;

    QUdpSocket* socket;

    void processRequest();
    QString process(const QString& request);
};
#endif // WIDGET_H

widget.cpp

构造对象的时候,参数可以是parent,也就是挂到对象树上(如果不写,之后手动delete即可)

image-20240925211236570

要线连接信号槽,再进行bind

一旦进行bind,就意味着请求可以收到了。

如果在连接信号槽之前bind,可能收到请求,而信号槽还没连接,此时请求就丢失了

#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("udp服务器");

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

    //绑定端口号
    //any表示不管几个网卡,都可以绑定上去
    if(!socket->bind(QHostAddress::Any, 8080))
    {
        QMessageBox::critical(this, "bind error", socket->errorString());
        return;
    }
}

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

void Widget::processRequest()
{
    //读取请求并解析
    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
    //data()返回QByteArray  QBtyeArray可以赋值给QString
    QString request = requestDatagram.data();

    //根据请求计算(此时是回显服务器, 收到什么响应什么)
    const QString& response = process(request);

    //把响应写给客户端
    //取出字节数组 发送到的地址 发送到的端口 都包含在requestDatagram
    QNetworkDatagram responseDatagram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());
    socket->writeDatagram(responseDatagram);

    //交互的信息 显式到界面
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort()) +
            "] req:" + request + ", resp: " + response;

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

QString Widget::process(const QString &request)
{
    return request;
}

Udp客户端

Qt Creator支持同时打开多个项目,但如果姓名中存在同名文件,就非常容易混淆

image-20240924215956667

客户端主动发起请求,界面设置一个输入框,一个发送按钮,一个显示服务器返回内容

image-20240924220714547

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();

private slots:
    void on_pushButton_clicked();
    void processResponse();
private:
    Ui::Widget *ui;
    QUdpSocket* socket;
};
#endif // WIDGET_H

widget.cpp

在Linux中,需要考虑一些阻塞问题,什么时候阻塞,什么时候解除阻塞。

而这里直接用信号槽机制,很方便。

#include "widget.h"
#include "ui_widget.h"
#include<QNetworkDatagram>
const QString SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 8080;

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::processResponse);

}

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


void Widget::on_pushButton_clicked()
{
    //获取输入框内容
    const QString& text = ui->lineEdit->text();

    //构造udp请求数据包
    //需要的参数是字节数组,要转换一下 IP地址也要转换一下
    QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);

    //发送请求数据
    socket->writeDatagram(requestDatagram);

    //发送的请求添加到列表框
    ui->listWidget->addItem("client say# " + text);

    //输入框内容清空
    ui->lineEdit->setText("");
}

void Widget::processResponse()
{
    //读取响应数据
    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
    QString response = responseDatagram.data();
    //响应到界面
    ui->listWidget->addItem("server say# " + response);
}

GIF 2024-9-25 21-31-21


http://www.kler.cn/news/325572.html

相关文章:

  • golang学习笔记24-文件操作
  • 实用好软-----电脑端 从视频中导出音频的方便工具
  • 微信小程序-数据模型与动态赋值
  • 女性向游戏的新战场:AI陪伴系统
  • CSS中的字体样式、文本样式、列表样式以及背景和渐变
  • VS Code、Git与自动化脚本的效能之旅
  • MATLAB GUI设计原则与实践
  • GPT带我学-设计模式18-访问者模式
  • Springboot项目-实战2-实现
  • 计数排序(counting sort)
  • 计算机毕业设计 饮食营养管理信息系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • 问题:vscode 打印中文时终端输出乱码
  • 快手:数据库升级实践,实现PB级数据的高效管理|OceanBase案例
  • (22)activeMQ部署
  • Linux基础命令mkfs详解
  • C语言基础之数组
  • 低空经济时代:无人机飞行安全要点详解
  • 汽车线束之故障诊断方案-TDR测试
  • Leetcode 3302. Find the Lexicographically Smallest Valid Sequence
  • Anaconda虚拟环境默认路径在C盘怎么更改
  • 【bash】将本地未合入 master 的分支,生成对应 patche 文件
  • 计算机毕业设计 办公用品管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • JavaScript中的函数定义
  • 位图:如何实现网页爬虫中的 URL 去重功能?
  • 网络通信(学习笔记)
  • 【重学 MySQL】四十二、单行子查询
  • 城市大脑:智慧城市的神经中枢——典型实践与经验启示
  • K8s安装部署(v1.28)--超详细(cri-docker作为运行时)
  • Spring Boot 3.x 配置 Spring Doc以及导入postman带图详解
  • 数据集-目标检测系列-鼠检测数据集 mouse >> DataBall