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

【Qt开发】网络运用

目录

前言:

1,UDP Socket

2,TCP Socket

3,HTTP Client


前言:

        Qt这里的网络部分说明都是建立在已经有Linux系统网络知识的基础上,这里不在详细说明。还有,Qt是分模块处理的,默认情况下Qt只链接了gui模块。若是要进行网络方面的代码编写,需要在项目配置文件(.pro文件)中的 QT  += core gui 中添加 network,让Qt链接网络模块,确保编译器和链接器知道在编译和链接过程中包含相应的库和头文件。

QT       += core gui network

1,UDP Socket

        Udp Socket主要的类有两个——QUdpSocket 和 QNetworkDatagram。

        QUdpSocket 表示一个 UDP 的 socket文件。

        QNetworkDatagram 表示一个 UDP 数据报。

        下面来实现一个 UDP Socket。服务端程序如下:

// widget.h头文件

class Widget : public QWidget

{

    Q_OBJECT

public:

    Widget(QWidget *parent = nullptr);

    ~Widget();

    void processRequest();   // 数据到来时的处理函数

    QString process(const QString& request);   // 响应

private:

    Ui::Widget *ui;

    QListWidget* listWidget;

    QUdpSocket* socket;

};

// widget.cpp源文件

Widget::Widget(QWidget *parent)

    : QWidget(parent)

    , ui(new Ui::Widget)

{

    ui->setupUi(this);

    listWidget = new QListWidget(this);

    listWidget->setMaximumSize(this->geometry().width(), this->geometry().height());

    listWidget->setMinimumSize(this->geometry().width(), this->geometry().height());

    socket = new QUdpSocket(this);

    this->setWindowTitle("服务器");

    // 先连接信号槽,后绑定端口

    // readyRead信号用于在UDP套接字有可读取的数据报到达时发出通知

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

    bool ret = socket->bind(QHostAddress::Any, 9090); // Any表示服务器将监听所有可用的网络接口

    if (!ret) {

        // 绑定失败

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

        return;

    }

}

void Widget::processRequest()

{

    // 读取请求

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

    QString request = requestDatagram.data();

    // 根据请求计算响应(这里设计的请求与响应完全一样)

    const QString& response = process(request);

    // 把响应写回到客户端

    QNetworkDatagram responseDatagram(response.toUtf8(),

        requestDatagram.senderAddress(), requestDatagram.senderPort());

    socket->writeDatagram(responseDatagram);

    // 显⽰打印⽇志

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

        QString::number(requestDatagram.senderPort())

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

    listWidget->addItem(log);

}

QString Widget::process(const QString& request)

{

    return request;

}

        客户端程序如下:

ui界面的设计

// widget.cpp源文件

// 定义服务器的 IP 和 端⼝

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

    this->setWindowTitle("客户端");

    socket = new QUdpSocket(this);

    connect(socket, &QUdpSocket::readyRead, this, [=]() {

        // 读取响应的数据

        const QNetworkDatagram responseDatagram = socket->receiveDatagram();

        QString response = responseDatagram.data();

        // 显示响应的数据

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

    });

}

void Widget::on_pushButton_clicked()

{

    // 构造请求数据

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

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

        SERVER_PORT);

    // 发送请求

    socket->writeDatagram(requestDatagram);

    // 消息添加到列表框中

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

    // 清空输⼊框

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

}

测试运行图

2,TCP Socket

        TCP Socket 核心类有两个——QTcpServer 和 QTcpSocket。

        QTcpServer 用于监听端口和获取客户端连接。

        QTcpSocket 用户客户端和服务器之间的数据交互。

        QByteArray 本质是一个字节数组,用于存储二进制数据和文本数据。它可以很方便的和 QString 进行相互转换。例如:

  • 使用 QString 的构造函数即可把 QByteArray 转成 QString。
  • 使用 QString 的 toUtf8 函数即可把 QString 转成 QByteArray。

        下面来实现一个 TCP Socket。服务端程序如下:

// widget.h头文件

class Widget : public QWidget

{

    Q_OBJECT

public:

    Widget(QWidget *parent = nullptr);

    ~Widget();

    void processConnection();

    const QString process(const QString request);

private:

    Ui::Widget *ui;

    QListWidget* listWidget;

    // 创建 QTcpServer

    QTcpServer* tcpServer;

};

// widget.cpp源文件

Widget::Widget(QWidget *parent)

    : QWidget(parent)

    , ui(new Ui::Widget)

{

    ui->setupUi(this);

    listWidget = new QListWidget(this);

    listWidget->setMaximumSize(this->geometry().width(), this->geometry().height());

    listWidget->setMinimumSize(this->geometry().width(), this->geometry().height());

    this->setWindowTitle("服务器");

    tcpServer = new QTcpServer(this);

    // 通过信号槽,处理客户端建⽴的新连接

    connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);

    // 监听端口

    bool ret = tcpServer->listen(QHostAddress::Any, 9090);

    if (!ret) {

        QMessageBox::critical(nullptr, "服务器启动失败", tcpServer->errorString());

        exit(1);

    }

}

void Widget::processConnection()

{

    // 获取到新的连接对应的socket

    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();

    QString log = QString("[") + clientSocket->peerAddress().toString()

        + ":" + QString::number(clientSocket->peerPort()) + "] 客户端上线";

    listWidget->addItem(log);

    // 通过信号槽, 处理收到请求的情况

    connect(clientSocket, &QTcpSocket::readyRead, this, [=]() {

        // 根据请求处理响应(这里设计的请求与响应一样)

        QString request = clientSocket->readAll();

        const QString& response = process(request);

        // 把响应写回客户端,并打印相关日志

        clientSocket->write(response.toUtf8());

        QString log = QString("[") + clientSocket->peerAddress().toString()

            + ":" + QString::number(clientSocket->peerPort()) + "] req: " +

            request + ", resp: " + response;

        listWidget->addItem(log);

    });

    // 通过信号槽, 处理断开连接的情况

    connect(clientSocket, &QTcpSocket::disconnected, this, [=]() {

        // 显示日志

        QString log = QString("[") + clientSocket->peerAddress().toString()

            + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线";

        listWidget->addItem(log);

        // 删除clientSocket。该方法不会立刻执行,而是在下一轮的事件循环中被销毁

        clientSocket->deleteLater();

    });

}

const QString Widget::process(const QString request)

{

    return request;

}

        客户端程序如下:

// widget.h头文件

class Widget : public QWidget

{

    Q_OBJECT

public:

    Widget(QWidget *parent = nullptr);

    ~Widget();

private slots:

    void on_pushButton_clicked();

private:

    Ui::Widget *ui;

    // 新增 QTcpSocket

    QTcpSocket* socket;

};

// widget.cpp源文件

Widget::Widget(QWidget *parent)

    : QWidget(parent)

    , ui(new Ui::Widget)

{

    ui->setupUi(this);

    this->setWindowTitle("客户端");

    socket = new QTcpSocket(this);

    // 和服务器建⽴连接

    socket->connectToHost("127.0.0.1", 9090);

    // 连接信号槽,处理服务器返回的响应

    connect(socket, &QTcpSocket::readyRead, this, [=]() {

        QString response = socket->readAll();

        qDebug() << response;

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

    });

    // 等待连接并确认连接是否出错

    if (!socket->waitForConnected()) {

        QMessageBox::critical(nullptr, "连接服务器出错", socket->errorString());

        exit(1);

    }

}

void Widget::on_pushButton_clicked()

{

    // 获取输⼊框的内容

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

    // 清空输⼊框内容

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

    // 把消息显示到界⾯上

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

    // 发送消息给服务器

    socket->write(text.toUtf8());

}

3,HTTP Client

        在网络方面,Qt只提供了HTTP客户端的库,没有提供HTTP服务端的库。HTTP本质上也是基于 TCP Socket 进行封装。Qt主要提供了三个类对此操作QNetworkAccessManager,QNetworkRequest,QNetworkReply

        QNetworkAccessManager 提供了 HTTP 的核心操作。

         QNetworkRequest 表示一个 HTTP 请求(不含body)。如果需要发送一个带有 body 的请求(比如 post),会在 QNetworkAccessManager 的 post 方法中通过单独的参数来传入 body。

        QUrl是Qt表示Url的类,QVariant类似于C语言中的void*,表示一个 “类型可变” 的值。其中的 QNetworkRequest::KnownHeaders 是一个枚举类型,常用取值如下:

        QNetworkReply 表示一个 HTTP 响应。

        此外,QNetworkReply 还有一个重要的信号 finished,它会在客户端收到完整的响应数据之后触发。代码运用如下:

// widget.h头文件

class Widget : public QWidget

{

    Q_OBJECT

public:

    Widget(QWidget *parent = nullptr);

    ~Widget();

private slots:

    void on_pushButton_clicked();

private:

    Ui::Widget *ui;

    // 新增属性

    QNetworkAccessManager* manager;

};

// widget.cpp源文件

Widget::Widget(QWidget *parent)

    : QWidget(parent)

    , ui(new Ui::Widget)

{

    ui->setupUi(this);

    // 实例化属性

    manager = new QNetworkAccessManager(this);

}

void Widget::on_pushButton_clicked()

{

    // 获取到输⼊框中的URL,构造QUrl对象

    QUrl url(ui->lineEdit->text());

    // 构造HTTP请求对象,发送GET请求

    QNetworkRequest request(url);

    QNetworkReply* response = manager->get(request); // get非阻塞函数

    // 通过信号槽来处理响应

    connect(response, &QNetworkReply::finished, this, [=]() {

        if (response->error() == QNetworkReply::NoError) {

            // 响应正确

            QString html(response->readAll());

            ui->plainTextEdit->setPlainText(html);

            // qDebug() << html;

        } else {

            // 响应出错

            ui->plainTextEdit->setPlainText(response->errorString());

        }

        // 对response进行释放

        response->deleteLater();

    });

}

http://www.xdnf.cn/news/14687.html

相关文章:

  • 项目中后端如何处理异常?
  • JAVA锁机制:对象锁与类锁
  • 一、什么是生成式人工智能
  • GPT-1 与 BERT 架构
  • MySQL之InnoDB存储引擎深度解析
  • 软件工程期末试卷填空题版带答案(共40道)
  • 【环境配置】在Ubuntu Server上安装5090 PyTorch环境
  • CVE-2024-6387漏洞、CVE-2025-26465漏洞、CVE-2025-26466漏洞 一口气全解决
  • 题解:P11501 [ROIR 2019] 探险队(Day 2)
  • 【软考高级系统架构论文】论无服务器架构及其应用
  • 在 `setup` 函数中使用 Vuex
  • 通过 Lambda + API Gateway + 外部 API 实现。
  • Django数据库迁移
  • LLM:重构数字世界的“智能操作系统”
  • Java面试题025:一文深入了解数据库Redis(1)
  • Docker高级管理--容器通信技术与数据持久化
  • 【ubuntu下小工具】Crontab定时任务进行数据备份和清理
  • 【AGI】突破感知-决策边界:VLA-具身智能2.0
  • 格兰泰勒棱镜透射光强曲线优化处理
  • 嵌入式开发之嵌入式系统架构如何搭建?
  • Java ArrayList集合和HashSet集合详解
  • day38 打卡
  • 基于Python、tkinter、sqlite3 和matplotlib的校园书店管理系统
  • 多线程八股
  • Shell脚本中和||语法解析
  • tkinter Text 组件学习指南
  • 创业知识概论
  • 机器学习流量识别(pytorch+NSL-KDD+多分类建模)
  • 深入解析BERT:语言分类任务的革命性引擎
  • 5G 浪潮:发展全景、困境突围与未来航向