Qt 访问网络

摘要:
1、 前言Qt使用QNetworkAccessManager访问网络。它的API是异步的,因此在访问网络时,不需要启动线程来执行线程中请求的代码。(然而,当有时需要阻塞时,这是一个问题。)需要注意的是,请求响应的对象QNetworkReply需要自己手动删除。通常,回复->在QNetworkAccessManager::finished signal的Cao函数中使用;删去

一、前言

Qt 中访问网络使用 QNetworkAccessManager,它的 API 是异步的,这样在访问网络的时候不需要启动一个线程,在线程里执行请求的代码。(但这一点在有时候需要阻塞时就是个麻烦了)

需要注意一点的是,请求响应的对象 QNetworkReply 需要我们自己手动的删除,一般都会在 QNetworkAccessManager::finished 信号的曹函数里使用 reply->deleteLater() 删除,不要直接 delete reply

二、示例一

#include <QDebug>
#include <QApplication>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QNetworkAccessManager *manager = new QNetworkAccessManager();
    QNetworkRequest request(QUrl("http://www.baidu.com"));
    QNetworkReply *reply = manager->get(request);

    int count = 0;

    QObject::connect(reply, &QNetworkReply::readyRead, [&] {
        qDebug() << QString(reply->readAll());
        qDebug() << ++count;
    });
    
    // 请求错误处理
    QObject::connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), [&] {
        qDebug() << reply->errorString();
    });

    // 请求结束时删除 reply 释放内存
    QObject::connect(reply, &QNetworkReply::finished, [&] {
        reply->deleteLater();
    });

    return app.exec();
}

仔细观察上面程序的输出结果,由于返回的数据比较大,readyRead 被调用了多次,而不是一次性就得到了请求的响应数据,这个特点在某些情况下很有用,例如下载 100M 的文件,多次读取肯定是合适的,因为读取后数据就会从 reply 中删除,不会导致占用太多内存。

但是在某些情况下却不太好用,例如读取一个响应 JSON 的数据,一般都不会太大,大的也就几十上百 K,如果一次得不到 JSON 的全部数据,多次读取的情况下想要拼出一个完整的 JSON 字符串不太容易,这时如果能一次性的得到响应的 JSON 数据是不是就很方便了呢?

三、示例二

要一次性读取到响应的数据可以在 QNetworkReply::finished 信号处理中进行,如下:

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QNetworkAccessManager *manager = new QNetworkAccessManager();
    QNetworkRequest request(QUrl("http://www.baidu.com"));
    QNetworkReply *reply = manager->get(request);

    // 请求错误处理
    QObject::connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), [&] {
        qDebug() << reply->errorString();
    });
    
    // 请求结束时一次性读取所有响应数据
    QObject::connect(reply, &QNetworkReply::finished, [&] {
        if (reply->error() == QNetworkReply::NoError) {
            qDebug() << reply->readAll();
        }

        reply->deleteLater();
    });

    return app.exec();
}

四、封装HTTP网络工具类

观察上面的程序,会发现很多代码都是重复的模版代码,例如

  • 创建 QNetworkRequest
  • 获取 QNetworkReply
  • 删除 QNetworkReply
  • 错误处理
  • 响应处理

大量的模版代码可以把它们封装成一个工具类,方便使用,参考下面 main() 函数里的调用,代码一下子看上去就清晰简单了很多。

main.cpp

#include "NetworkUtil.h"

#include <QDebug>
#include <QApplication>
#include <QNetworkAccessManager>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QNetworkAccessManager *manager = new QNetworkAccessManager();

    // 访问 baidu
    NetworkUtil::get(manager, "http://www.baidu.com", [](const QString &response) {
        qDebug() << response;
    });  // 默认的异步方式

    // 访问 163
    NetworkUtil::get(manager, "http://www.163.com", [](const QString &response) {
        qDebug() << response;
    }, NULL, true, "GB2312"); // 同步方式

    return app.exec();
}

NetworkUtil.h

#ifndef NETWORKUTIL_H
#define NETWORKUTIL_H

#include <functional>

class QString;
class QNetworkAccessManager;

class NetworkUtil {
public:
    /**
     * @brief 使用 GET 访问网络
     *
     * @param manager QNetworkAccessManager 对象
     * @param url 需要访问的 URL
     * @param successHandler 访问成功的 Lambda 回调函数
     * @param errorHandler 访问失败的 Lambda 回调函数
     * @param async 默认为异步方式,false 以设置为同步(阻塞式)方式
     * @param encoding 读取响应数据的编码
     */
    static void get(QNetworkAccessManager *manager,
                    const QString &url,
                    std::function<void (const QString &)> successHandler,
                    std::function<void (const QString &)> errorHandler = NULL,
                    const bool &async = true,
                    const char *encoding = "UTF-8");
};

#endif // NETWORKUTIL_H

NetworkUtil.cpp

#include "NetworkUtil.h"
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QTextStream>
#include <QEventLoop>

void NetworkUtil::get(QNetworkAccessManager *manager,
                      const QString &url,
                      std::function<void (const QString &)> successHandler,
                      std::function<void (const QString &)> errorHandler,
                      const bool &async,
                      const char *encoding) {
    QUrl urlx(url);
    QNetworkRequest request(urlx);
    QNetworkReply *reply = manager->get(request);

    // 请求错误处理
    QObject::connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), [=] {
        if (NULL != errorHandler) {
            errorHandler(reply->errorString());
        }
    });

    // 请求结束时一次性读取所有响应数据
    QObject::connect(reply, &QNetworkReply::finished, [=] {
        if (reply->error() == QNetworkReply::NoError) {
            // 读取响应数据
            QTextStream in(reply);
            QString result;

            in.setCodec(encoding);
            while (!in.atEnd()) {
                result += in.readLine();
            }

            successHandler(result);
        }

        reply->deleteLater();
    });

    // 异步,还是同步阻塞
    if(!async) {
        // 使用QEventLoop阻塞
        QEventLoop eventLoop;
        QObject::connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
        eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
    }
}

Qt 的网络操作类是异步(非阻塞的),所以这里使用 QEventLoop 来手动阻塞以实现同步。

这里暂时只介绍了一次性读取时 Get 的封装,后面考虑实现 Get 多次读取的封装,Post 请求的封装等。


参考:

Qt 访问网络

Qt实现同步(阻塞式)http get等网络访问操作


免责声明:文章转载自《Qt 访问网络》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇转: mysql create view 创建视图bat批处理下如何像shell一样将命令执行的效果赋值给变量下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

OpenGL ES着色器语言----------------储存修饰符

一、存储修饰符 本地变量只能使用存储修饰符const。 函数参数只能用const。函数返回值类型和结构体字段不要使用const。 从一个运行时着色器到下一个运行时着色器之间进行数据类型通信是不存在的。这阻止了同一个着色器在多个顶点和片元之间同时执行。 没有存储修饰符或仅仅使用const修饰符的全局变量,可能在main()执行前进行初始化。Uniforms...

Qt532.QString::split()

1、效果 和 JS里面 貌似是一样的 1.1、QString 编码转换(https://www.cnblogs.com/CodeSkill/p/5082447.html) 2、代码: voidMainWindow::on_pushButton_2_clicked() { QString str = "A B 10 2"; QStringLi...

PIE-SDK For C++打开静止卫星数据

1.功能简介    静止卫星是位于地球赤道上空约3.58万km处,与地面始终保持相对静止的卫星,静止卫星的特点是覆盖区域广,具有很强的机动灵活性,能够对特定区域进行分钟级高重复观测,可快速监测灾害目标的动态变化。目前风云2系列、风云4系列、葵花(Himawari)系列、高分4卫星均为静止卫星。 [静止卫星位置示意图] [卫星运行轨迹图] [F...

C++编译遇到参数错误(cannot convert parameter * from 'const char [**]' to 'LPCWSTR')

转:http://blog.sina.com.cn/s/blog_9ffcd5dc01014nw9.html 前面的几天一直都在复习着被实习落下的C++基础知识。今天在复习着上次创建的窗口程序时,出现了一个错误,百思不得其解。因为是同样的代码,上次的都能顺利的通过编译,这次自己新建了一个工程结果就有一个错误出现,是在调用Create()函数时,传参数出现问...

&amp;lt;转&amp;gt;boost::any的用法、优点和缺点以及源代码分析

01.#include <iostream> 02.#include <list> 03.#include <boost/any.hpp> 04. 05.typedef std::list<boost::any> list_any; 06. 07.//关键部分:可以存放任意类型的对象 08.void fill...

TwelveMonkeys处理图片

TwelveMonkeys处理图片 项目中用到图片处理功能,主要是调整tiff图片的dpi,并进行图片压缩。 之前用的是jai的方式处理的,主要jar包是jai_imageio、jai_codec等。 因为jai的包名为com.sun开头。甲方认为后续如果调整jdk可能会出问题(我了解的情况是:jai作为单独jar包引入,并未和jdk绑定,只是包名是com...