当前位置: 首页 > 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();

    });

}

相关文章:

  • GitHub OAuth 认证示例
  • Linux - 安装 git(sudo apt-get)
  • Vue 简写形式全解析:清晰记忆指南
  • 《map和set的使用介绍》
  • 八大架构宪法 - 技术使用指导说明文档
  • OBS --- 录像地址
  • Mac Parallels Desktop Kali 2025 代理设置
  • 区块链是什么
  • 学习C++、QT---02(创建第一个C++项目、命名空间的讲解)
  • Transformer:现代自然语言处理的革命性架构
  • Java大厂面试攻略:Spring Boot与微服务架构深度剖析
  • yolov11转ncnn
  • 人人都是音乐家?腾讯开源音乐生成大模型SongGeneration
  • [论文阅读] 人工智能 + 软件工程 |
  • ARCGIS国土超级工具集1.6更新说明
  • 面向互联网大厂Java岗位面试:Spring Boot与微服务架构的深入探讨
  • Filecoin系列 - IPLD 技术分析
  • 吐槽之前后端合作开发
  • 李宏毅2025《机器学习》第三讲-AI的脑科学
  • “荔枝使”的难题怎么破:A*运输路径算法编程实践
  • 做普工招聘网站/网站设计报价方案
  • 动态网站开发过程ppt/企业推广方式
  • 做儿童文学有哪些的网站/广告公司招聘
  • 河北网络公司网站建设/河北seo技术
  • 怎样做公司的网站建设/店铺如何运营和推广
  • 网站推广策划评估工具7/郑州seo建站