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

【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.lqws.cn/news/463321.html

相关文章:

  • “氢键本征型材料 + 柔性电容应变片”方案分析
  • NW849NX721美光固态闪存NX745NX751
  • C++中的指针与引用
  • ProtoBuf:proto3 语法详解
  • 三甲医院AI医疗样本数据集分类与收集全流程节点分析(下)
  • 【appium】2.初始连接脚本配置
  • React扩展知识点
  • 使用Node.js开发服务端接口
  • 【赵渝强老师】使用mysqldump备份MySQL
  • 燕山大学多核程序设计实验(25最新版)
  • 数据分析核心指标体系:从求和、计数到比较的全维度计算方法
  • 一站式了解责任链模式
  • Qt实战:自定义二级选项框 | 附完整源码
  • 【Linux第四章】gcc、makefile、git、GDB
  • 【日志系统-时间戳】
  • 告别线程爆炸:我如何用 Spring WebFlux 构建一个端到端响应式应用
  • ad24智能pdf输出的装配图没有四个边角那里的圆孔
  • 面试题-ts中的typeof
  • 读者写者问题与读写锁自旋锁
  • OpenAI与微软的未来合作之路:充满挑战的AI竞赛与共赢
  • STM32F103C8T6 学习笔记摘要(二)
  • Knife4j 使用详解
  • (详细介绍)线性代数中的零空间(Null Space)
  • GitHub Copilot快捷键
  • JVM(8)——详解分代收集算法
  • linux生产环境下根据关键字搜索指定日志文件命令
  • Android多进程数据共享:SharedPreferences替代方案详解
  • RocketMQ--为什么性能不如Kafka?
  • 黑马头条-数据管理平台
  • Codeforces Round 1028 (Div. 2) A-C