【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);
}

运行结果如下:

结束语

感谢你的阅读

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

相关推荐
倒头就睡的小比特4 天前
算法竞赛C++常用的STL
c++·算法
weilx12344 天前
C++笔记-文件IO-<fcntl.h>
c++
未济4 天前
linux 配置环境变量
linux
傲世仙尊4 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
虎头金猫4 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
Smileyqp沛沛4 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++
_艾伦 耶格尔.4 天前
进程间通信
linux
旖旎夜光4 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
Liuqy-054 天前
Linux IO编程——静态库、动态库
linux