【Qt】Qt 网络 | UDP Socket

文章目录

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

核心API

Qt 的 UDP Socket 主要的类有两个 QUdpSocket 和 QNetworkDatagram

因为是 UDP 是面向数据报的,QNetworkDatagram 就是对 数据报的封装,内容包括:对端发送的数据,对端的 IP 和 端口号

QUdpSocket 表示一个 UDP 的 socket 文件

名称 类型 说明 对标原生API
bind(const QHostAddress &address, quint16 port = 0) 方法 绑定指定的 IP地址 和 端口号 bind
receiveDatagram() 方法 返回 QNetworkDatagram 读取一个 UDP 数据报 recvfrom
writeDatagram(const QNetworkDatagram &datagram) 方法 发送一个 UDP 数据报 sendto
writeDatagram(const char *data, qint64 size, const HostAddress &address, quint16 port) 方法 data是要发送的数据,size是数据的长度,address是目标 IP地址,port是目标端口号 sendto
readyRead 信号 在收到数据并准备就绪后触发 类似于 IO 多路复用的通知机制

QNetworkDatagram 表示一个 UDP 数据报

名称 类型 说明 对标原生API
QNetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress = QHostAddress(), quint16 port = 0) 构造函数 通过 QByteArray,目标 IP地址,目标端口号构造一个 UDP 数据报,通常用于发送数据 无
data() 方法 获取数据报内部持有的数据,返回 QByteArray 无
senderAddress() 方法 获取数据报中包含的对端的IP地址 recvfrom 包含了该功能
senderPort() 方法 获取数据报中包含的对端的端口号 recvfrom 包含了该功能

代码示例

通过模拟 客户服务器的UDP通信 熟悉上述方法使用

客户端通过输入栏获取数据,点击按钮发送给服务端

服务端接受客户端数据,并返回相同数据,验证通信无误

服务器

  1. 创建界面,包含一个 QListWidget 用来显示消息
  2. 创建 QUdpSocket 成员,修改 widget.h
cpp 复制代码
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
//槽函数
public slots:
    void handleRequest();//处理客户端请求
private:
    QString process(const QString &request);//业务处理逻辑
private:
    Ui::Widget *ui;
    QUdpSocket *socket;//套接字
};
  1. 编写 widget.cpp,实现相关方法

因为 UDP 是无连接的,绑定端口号后就可以接受数据了,所以要先对 readReady信号连接槽函数

逻辑如下:1. 设置窗口标题 2. 创建套接字 3. 连接信号槽 4. 绑定端口号

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //1. 设置窗口标题
    this->setWindowTitle("服务端");
    //2. 创建套接字
    socket = new QUdpSocket(this);
    //UDP绑定完就开启服务了,所以需要先连接信号槽
    //3. 连接信号槽
    connect(socket, &QUdpSocket::readyRead, this, &Widget::handleRequest);
    //4. 绑定,服务端绑定任意IP,端口号指定为9090
    bool ret = socket->bind(QHostAddress::Any, 9090);
    if(!ret)
    {
        QMessageBox::critical(this, "错误", "绑定套接字失败");
        return;
    }

}

编写 readReady 对应槽函数

逻辑如下:1. 获取用户数据报 2. 提取其中数据 3. 业务处理 4. 返回响应

cpp 复制代码
void Widget::handleRequest()
{
    //1. 获取用户数据报
    const QNetworkDatagram requestDatagram = socket->receiveDatagram();
    //2. 提取其中数据
    QString request = requestDatagram.data();
    //3. 业务处理逻辑
    QString responce = process(request);
    //4. 记录客户端数据
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())
                + "]  request:" + request + "  responce:" + responce;
    ui->listWidget->addItem(log);
    //5. 返回响应
    QNetworkDatagram responceDatagram(responce.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());
    socket->writeDatagram(responceDatagram);
}

此处服务器的业务处理只是简单的回显数据

cpp 复制代码
QString Widget::process(const QString &request)
{
    return request;
}

客户端

  1. 创建界面。包含 一个 QLineEdit,QPushButton, QListWidget
    通过布局管理器搭配,规范界面
  2. 声明全局常量:服务端的 IP地址 和 端口号
cpp 复制代码
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 9090
  1. 创建 QUdpSocket 成员,修改 widget.h,定义成员
cpp 复制代码
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private slots:
    //按钮点击槽函数
    void on_pushButton_clicked();
    //客户端业务处理逻辑
    void handleRequest();
private:
    Ui::Widget *ui;
    QUdpSocket *socket;
};
  1. 修改 widget.cpp,初始化 socket
    客户端不需要绑定端口号
cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton->setShortcut(QKeySequence(Qt::Key_Return));
    //设置窗口标题
    this->setWindowTitle("客户端");
    //创建套接字
    socket = new QUdpSocket(this);
    //连接信号槽
    connect(socket, &QUdpSocket::readyRead, this, &Widget::handleRequest);
}

按钮点击槽函数

逻辑如下:1. 获取输入栏文本 2. 记录发送信息 3. 将消息发送给服务端 4. 清空输入栏

cpp 复制代码
void Widget::on_pushButton_clicked()
{
    //1. 获取输入栏的文本
    QString text = ui->lineEdit->text();
    QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);
    //2. 记录发送信息
    QString message = "发送:" + text;
    ui->listWidget->addItem(message);
    //3. 将消息发送给服务端
    socket->writeDatagram(requestDatagram);
    //4. 将输入栏清空
    ui->lineEdit->clear();
}

客户端业务处理函数

cpp 复制代码
void Widget::handleRequest()
{
    //1. 获取服务端返回的数据报
    QNetworkDatagram responceDatagram = socket->receiveDatagram();
    QString responce = responceDatagram.data();
    //2. 显示消息
    ui->listWidget->addItem("接收:" + responce);
}

运行结果如下:

结束语

感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。

相关推荐
星恒随风3 分钟前
MySQL 从零入门(六):复合查询详解——多表查询、自连接、子查询与 UNION
linux·服务器·数据库·mysql
少司府13 分钟前
嵌入式入门篇(二):初识STM32F103ZET6
linux·python·stm32·单片机·嵌入式硬件·mcu·嵌入式软件
无足鸟ICT1 小时前
【RHCA+】IO调度算法
linux·服务器
火云代理1 小时前
Python 使用代理 IP 前,先弄清 Requests 的配置来源与验证方法
网络·python·网络协议·tcp/ip·ip
wacsii1 小时前
【Linux进程(四)】命令行参数与环境变量
linux·服务器
无奈笑天下1 小时前
银河麒麟上lsblk命令教程详解
linux·运维·服务器·数据库
如夏倾城1 小时前
QUIC——HTTP/3 的唯一底层传输协议 及 0-RTT恢复
网络·网络协议·tcp/ip·安全·http
YLCHUP2 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
xiaoye-duck2 小时前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型
科研邪修法3 小时前
培训PDF保护配置:把需求转换成可验收权限
linux·服务器·网络