前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Qt开源网络库[4]-原理篇下

Qt开源网络库[4]-原理篇下

作者头像
Qt君
发布2019-07-15 15:31:35
8400
发布2019-07-15 15:31:35
举报
文章被收录于专栏:跟Qt君学编程跟Qt君学编程

上一篇分析到原理上,主要讲述的是builder模式设计与HttpService,HttpRequest的原理和实现。本篇将会讲到HttpResponse的响应机制与自动推导接收者(槽函数)的实现。

先从问题入手,看下面onResponse代码,为什么会存在第三个参数HttpResponse::SupportMethod type呢?响应只需接收者与槽函数就可以了吧。这里写槽函数是因为用得最多,实际上也可以写成信号,相当于再次转发。

代码语言:javascript
复制
HttpRequest &onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type = HttpResponse::AutoInfer);

onResponse函数

1. SupportMethod枚举

(1) 写的是支持const char *slot的类型;

(2) 当使用AutoInfer则系统会自动通过槽函数推导出对应的枚举值。

代码语言:javascript
复制
/*
 * Support Reflex Method
 * default: AutoInfer
 * AutoInfer: Automatic derivation based on type
 */
enum SupportMethod {
    AutoInfer,
    onResponse_QNetworkReply_A_Pointer,    /* method: void function(QNetworkReply* reply); Is_AutoInfer: true */
    onResponse_QByteArray,                 /* method: void function(QByteArray data); Is_AutoInfer: true */
    onResponse_QVariantMap,                /* method: void function(QVariantMap map); Is_AutoInfer: true */
    onDownloadProgress_qint64_qint64,      /* method: void function(qint64 bytesReceived, qint64 bytesTotal); Is_AutoInfer: true */
    onError_QNetworkReply_To_NetworkError, /* method: void function(QNetworkReply::NetworkError error); Is_AutoInfer: true */
    onError_QString,                       /* method: void function(QString errorString); Is_AutoInfer: true */
    onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer, /* method: void function(QNetworkReply::NetworkError error, QNetworkReply* reply); Is_AutoInfer: true */
    onError_QString_QNetworkReply_A_Poniter/* method: void function(QString errorString, QNetworkReply* reply); Is_AutoInfer: true */
};

SupportMethod枚举

2. SupportMethod的使用

(1) 截取一段上面SupportMethod枚举的注释可以看到"method: void function(QNetworkReply* reply); Is_AutoInfer: true";

(2)onResponse_QNetworkReply_A_Pointer枚举值的槽函数可以支持注释上的格式,就如行1写法。

代码语言:javascript
复制
onResponse(this, SLOT(finish(QNetworkReply*)));
onResponse(this, SLOT(finish(QByteArray)));
onResponse(this, SLOT(finish(QVariantMap)));
onResponse(this, SLOT(finish(QNetworkReply::NetworkError)));
onResponse(this, SLOT(QString));
onResponse(this, SLOT(QNetworkReply::NetworkError, QNetworkReply*));
onResponse(this, SLOT(QString, QNetworkReply*));

onResponse实际用法

3. 自动推导类型的应用

(1) 调用onResponse则会将type,receiver与slot存放在m_slotsMap私有变量里面,方便提供给HttpResponse类绑定QNetworkReply的信号使用;

(2) 由于onError的slot都支持自动推导类型,则它也是调用onResponse。

代码语言:javascript
复制
HttpRequest &HttpRequest::onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type)
{
    m_slotsMap.insert(NUMBER_TO_STRING(type), {{slot, receiver)}});
    return *this;
}

HttpRequest &HttpRequest::onError(const QObject *receiver, const char *slot)
{
    return onResponse(receiver, slot, HttpResponse::AutoInfer);
}

onResponse与onError实现

4. HttpResponse的构造函数实现

(1) 当HttpRequest类装载完毕调用exec则会实例化HttpResponse类,传递的参数为QNetworkReply与HttpRequest的m_slotsMap;

(2) HttpResponse主要的作用为二次处理QNetworkReply(信号的转发)。

代码语言:javascript
复制
HttpResponse::HttpResponse(QNetworkReply *parent, const QMultiMap<QString, QMap<QString, const QObject *> > &slotsMap)
    : QNetworkReply(parent), m_slotsMap(slotsMap)
{
    slotsMapOperation(m_slotsMap);

    connect(parent, SIGNAL(finished()), this, SLOT(onFinished()));
    connect(parent, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
    connect(parent, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));

    if (!QNetworkConfigurationManager().isOnline())
        onError(QNetworkReply::UnknownNetworkError);
}

HttpResponse构造函数

5. slotsMapOperation的实现

HttpResponse构造时候则会调用slotsMapOperation函数,该函数主要作用是绑定HttpRequest的onResponse的槽函数,使得网络响应到来时候能够响应槽函数达到异步的效果。

(1) methodParams[key].value("signal")的methodParams是一个预先设定的容器;

(2) 这里的key实则是onResponse里面SupportMethod枚举的值,只是被转换为字符串;

(3) 用key来查找(methodParams)到对应的数据,这里查找的是信号属性;

(4) 查找到该信号属性则绑定它。

代码语言:javascript
复制
void HttpResponse::slotsMapOperation(QMultiMap<QString, QMap<QString, const QObject *> > &slotsMap)
{
    autoInfterConvertedSupportMethod(slotsMap);

    QMapIterator<QString, QMap<QString, const QObject *> > iter(slotsMap);
    while (iter.hasNext()) {
        iter.next();
        const QString &key = iter.key();
        const QMap<QString, const QObject *> &slotMap = iter.value();

        const QObject *receiver = slotMap.first();
        const QString &receiverSlot = slotMap.firstKey();

        if (methodParams.contains(key)) {
            connect(this,
                    methodParams[key].value("signal").toString().toStdString().data(),
                    receiver,
                    receiverSlot.toStdString().data(),
                    Qt::QueuedConnection);
        }
    }
}

slotsMapOperation函数

6. methodParams的预定义

methodParams看起来有点奇怪,但一层一层分解就可以看清楚,主要该值复杂是在容器里面嵌套了一个容器。

(1) 第一个容器的key为保存SupportMethod枚举值的字符串,N2S宏为将数字转为字符串;

(2) 第二个容器保存的是types,signal,isAutoInfer字段信息。

代码语言:javascript
复制
static const QMap<QString, QMap<QString, QVariant>> methodParams =
{
    {
        N2S(HttpResponse::onResponse_QNetworkReply_A_Pointer),
        {
            {"types", QStringList({T2S(QNetworkReply*)})},
            {"signal", SIGNAL(finished(QNetworkReply*))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onResponse_QByteArray),
        {
            {"types", QStringList({T2S(QByteArray)})},
            {"signal", SIGNAL(finished(QByteArray))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onResponse_QVariantMap),
        {
            {"types", QStringList({T2S(QVariantMap)})},
            {"signal", SIGNAL(finished(QVariantMap))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onDownloadProgress_qint64_qint64),
        {
            {"types", QStringList({T2S(qint64), T2S(qint64)})},
            {"signal", SIGNAL(downloadProgress(qint64, qint64))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onError_QNetworkReply_To_NetworkError),
        {
            {"types", QStringList({T2S(QNetworkReply::NetworkError)})},
            {"signal", SIGNAL(error(QNetworkReply::NetworkError))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onError_QString),
        {
            {"types", QStringList({T2S(QString)})},
            {"signal", SIGNAL(error(QString))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onError_QNetworkReply_To_NetworkError_QNetworkReply_A_Pointer),
        {
            {"types", QStringList({T2S(QNetworkReply::NetworkError), T2S(QNetworkReply*)})},
            {"signal", SIGNAL(error(QNetworkReply::NetworkError, QNetworkReply*))},
            {"isAutoInfer", true}
        }
    },
    {
        N2S(HttpResponse::onError_QString_QNetworkReply_A_Poniter),
        {
            {"types", QStringList({T2S(QString), T2S(QNetworkReply*)})},
            {"signal", SIGNAL(error(QString, QNetworkReply*))},
            {"isAutoInfer", true}
        }
    },
};

methodParams类型

7. 从"types"到推导槽函数类型

(1) getSupportMethod传入的容器参数为槽函数(QString)与接收者对象(const QObject*);

(2) 槽函数的传入,由于传入的是字符串则可以自动解析到槽的参数类型;

(3) extractSlot就是转换槽函数的数据类型为QStringList;

(4) 然后methodParams逐一判断是否有存在相同的"types"的值,如果存在,则自动找到methodParams的key值。

代码语言:javascript
复制
/* from slotMap get [SupportMethod] */
static QString getSupportMethod(const QMap<QString, const QObject *> &slotMap) {

    QMapIterator<QString, QMap<QString, QVariant>> iter(methodParams);

    QString receiverSlot = slotMap.firstKey();
    QString slot;
    QStringList slotTypes;
    extractSlot(receiverSlot, slot, slotTypes);

    while (iter.hasNext()) {
        iter.next();
        QString key = iter.key();
        QMap<QString, QVariant> value = iter.value();
        if (slotTypes == value.value("types").toStringList()) {
            return key;
        }
    }

    return "";
}

getSupportMethod函数

8. 信号的转发机制

(1) 在"5. slotsMapOperation的实现"连接的信号与槽函数,哪里的连接信号则是下买你所描述的信号;

代码语言:javascript
复制
signals:
    void finished(QNetworkReply *reply);
    void finished(QByteArray data);
    void finished(QVariantMap map);
    void error(QString errorString);
    void error(QNetworkReply::NetworkError error);
    void error(QString errorString, QNetworkReply *reply);
    void error(QNetworkReply::NetworkError error, QNetworkReply *reply);
    void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);

HttpResponse信号

(2) 下列为onFinished函数它在构造函数时候就已经绑定了QNetworkReply的finished信号,而其他信号也被同样的方式绑定。当对应的finished信号被发射的时候,就会触发先前已经绑定与传入的槽函数。就实现了信号的转发功能。

代码语言:javascript
复制
void HttpResponse::onFinished()
{
    QNetworkReply *reply = (QNetworkReply *)this->parent();
    if (m_slotsMap.contains(N2S(SupportMethod::onResponse_QNetworkReply_A_Pointer))) {
        emit finished(reply);
    }
    else if (m_slotsMap.contains(N2S(SupportMethod::onResponse_QByteArray))) {
        QByteArray result = reply->readAll();
        emit finished(result);
        reply->deleteLater();
    }
    else if (m_slotsMap.contains(N2S(SupportMethod::onResponse_QVariantMap))) {
        QByteArray result = reply->readAll();
        emit finished(QJsonDocument::fromJson(result).object().toVariantMap());
        reply->deleteLater();
    }
}

onFinished槽函数

最后在实现HttpResponse时实现比较繁琐,但是为了方便以后扩展需要。后续还会支持lambda表达式。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Qt君 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档