Qt中UDP回显服务器和客户端

Qt中UDP回显服务器和客户端

在进⾏⽹络编程之前, 需要在项⽬中的 .pro ⽂件中添加 network 模块.

添加之后要⼿动编译⼀下项⽬, 使 Qt Creator 能够加载对应模块的头⽂件

UDP Socket

核⼼ API 概览

主要的类有两个. QUdpSocket 和 QNetworkDatagram

QUdpSocket 表⽰⼀个 UDP 的 socket ⽂件.

QNetworkDatagram 表⽰⼀个 UDP 数据报.

回显服务器

  1. 创建界⾯, 包含⼀个 QListWidget ⽤来显⽰消息.
  1. 创建 QUdpSocket 成员

修改 widget.h

C++ 复制代码
#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, 完成 socket 后续的初始化

⼀般来说, 要先连接信号槽, 再绑定端⼝.

如果顺序反过来, 可能会出现端⼝绑定好了之后, 请求就过来了. 此时还没来得及连接信号槽. 那么这个请求就有可能错过了

C++ 复制代码
#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;
    }
}
  1. 实现 processRequest , 完成处理请求的过程

• 读取请求并解析

• 根据请求计算响应

• 把响应写回到客⼾端

C++ 复制代码
//服务器最核心的逻辑
void Widget::processRequest()
{
    //1.读取请求分析
    const QNetworkDatagram& requestDatagram=socket->receiveDatagram();
    QString request=requestDatagram.data();
    //2.根据请求计算响应
    const QString& response=process(request);
    //3.把响应写道客户端
    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);
}
  1. 实现 process 函数
C++ 复制代码
QString Widget::process(const QString &request)
{
    return request;
}

💡 "根据请求处理响应" 是服务器开发中的最核⼼的步骤.

⼀个商业服务器程序, 这⾥的逻辑可能是⼏万⾏⼏⼗万⾏代码量级的

此时, 服务器程序编写完毕.

但是直接运⾏还看不出效果. 还需要搭配客⼾端来使⽤.

回显客⼾端

  1. 创建界⾯. 包含⼀个 QLineEdit , QPushButton , QListWidget

• 先使⽤⽔平布局把 QLineEdit 和 QPushButton 放好, 并设置这两个控件的垂直⽅向的sizePolicy 为 Expanding

• 再使⽤垂直布局把 QListWidget 和上⾯的⽔平布局放好.

• 设置垂直布局的 layoutStretch 为 5, 1 (当然这个尺⼨⽐例根据个⼈喜好微调).

  1. 在 widget.cpp 中, 先创建两个全局常量, 表⽰服务器的 IP 和 端⼝
C++ 复制代码
//定义两个常量,表示服务器的地址和端口号
const QString&SERVER_IP="127.0.0.1";
const quint16 SERVER_PORT=9090;
  1. 创建 QUdpSocket 成员

修改 widget.h, 定义成员

C++ 复制代码
#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, 初始化 socket

C++ 复制代码
#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
  1. 给发送按钮 slot 函数, 实现发送请求.
C++ 复制代码
void Widget::on_pushButton_clicked()
{
    //获取到输入框的内容
    const QString& text=ui->lineEdit->text();
    //构造UDP的请求数据
    QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);
    //发送请求数据
    socket->writeDatagram(requestDatagram);
    //把发送的数据添加到列表框
    ui->listWidget->addItem("客户端说:"+text);
    //把输入框的内容清空一下
    ui->lineEdit->setText("");
}
  1. 再次修改 Widget 的构造函数, 通过信号槽, 来处理服务器的响应.
C++ 复制代码
 	//通过信号槽来处理,来处理服务器返回的数据
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processResponse);

void Widget::processResponse()
{
    //通过这个函数来处理收到的响应
    //1.读取到响应数据
    const QNetworkDatagram& responseDatagram=socket->receiveDatagram();
    QString response=responseDatagram.data();
    //2.把响应数据显示到界面上
    ui->listWidget->addItem("服务器说:"+response);
}
相关推荐
DFT计算杂谈3 小时前
服务器通过pip安装Kimi Code CLI 和使用
运维·服务器·pip
杨云龙UP7 小时前
MySQL 数据库常用备份工具对比:mysqldump、mydumper 与 XtraBackup
linux·运维·服务器·数据库·mysql·dba·备份恢复
happymagic8 小时前
java spring boot做的jar包程序,如何实现自动运行启动
java·运维·服务器·spring boot·jar
Dr.kangder9 小时前
嵌入式处理器仿真技术——Hypervisor 原理与实践
服务器·嵌入式硬件·架构·嵌入式
星蓝_starblue11 小时前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
云飞云共享云桌面14 小时前
广东精密机械设备工厂8-10人共享一台SolidWorks设计服务器
运维·服务器·3d·自动化·汽车·制造
jieyucx14 小时前
【高级利用】条件竞争与逻辑漏洞:与服务器赛跑的艺术
android·运维·服务器·web安全·文件上传
tianyuanwo14 小时前
服务器OS组件研发效能度量可视系统——顶层方案设计与阶段性规划
运维·服务器·度量系统
初级炼丹师(爱说实话版)14 小时前
Ubuntu服务器配置docker
服务器·ubuntu·docker
机建狂魔15 小时前
Codex 接入第三方模型 API 实战:以 Mimo 为例
java·服务器·数据库·ai·ai编程·codex