Qt网络编程的命令模式:把网络命令封装成类

命令模式是编程设计模式中的一种,这里介绍命令模式在Qt网络编程中的使用,讲述如何实现“一个类就是一个命令”的设计思想。

基本思想

  1. 把向服务器发起的请求抽象为一个C++类,相似的请求可以封装为同一个类,通过操作类型来区分。(命令的操作类型在服务器端需用,在客户端解析服务器反馈的数据时也需用。);
  2. 每增加一个网络命令,就增加一个C++类,这些命令类有共同的基类;
  3. 在命令基类里声明了接口函数(C++中为纯虚函数)供每个命令子类独立实现;
  4. 当使用命令时,只需要构造出命令子类(等号左边为基类指针变量,等号右边通过new关键字创建命令子类),设置好命令执行需要的相关参数,最后调用命令执行函数exec()即可向服务器发送数据。

类图.png

命令基类

本文福利, 免费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

commandabstract .h

#ifndef COMMANDABSTRACT_H
#define COMMANDABSTRACT_H

#include <QObject>
#include <QVariantHash>
class QTcpSocket;
class CommandAbstract : public QObject
{
    Q_OBJECT
public:
    explicit CommandAbstract(QObject *parent = 0);
    //接口:准备要发送给服务器的数据
    //参数:cmdArgs用于传递要发送给服务器的数据(或叫做命令参数)
    //说明:在命令子类中实现该接口,并将命令参数保存到m_sendingData成员变量中
    //(m_sendingData为最终发送给服务器的数据)
    virtual void prepareSendingData(const QVariantHash& cmdArgs) = 0;
    //接口:解析来自服务器的响应数据,保存到m_parsedResponseData成员变量中
    //参数:data为从服务器读取到的响应数据
    //说明:在命令子类中实现该接口,每个网络命令都有不同的解析方式,因此这里抽象为接口
    virtual void parseResponseData(const QByteArray& data) = 0;
    //获取要发送给服务器的数据m_sendingData
    virtual QByteArray getSendingData() const;
    //设置要发送给服务器的数据m_sendingData
    virtual void setSendingData(const QByteArray& data);
    //执行命令,即:向服务器发送数据m_sendingData
    void exec();
    //设置QTcpSocket指针到命令类中,以实现网络通信功能
    void setTcpSocket(QTcpSocket* tcpSocket);
    //设置命令序列,记录命令编号,根据需要设置
    void setCmdIndex(int index);
    //设置命令操作类型(当相同的命令子类需要实现不同的功能时使用,如:当要使用相同的
    //命令子类发送不同的数据到服务器时,通过操作类型来区分服务器响应的数据)
    void setOperType(int operType);
protected:
    int getCmdIndex() const;
    //获取命令操作类型
    int getCmdOperType() const;
signals:
    //通过信号对外通知命令执行数据m_parsedResponseData
    void infoResultData(QVariantHash& parsedResponseData,QString& errorString);
public slots:
    //当接收到网络数据响应时执行该槽函数
    void onReadyRead();
private:
    QVariantHash m_cmdData;//命令参数
    QByteArray m_sendingData;//最终发送给服务器的命令数据
    QVariantHash m_parsedResponseData;//解析之后的服务器响应数据
    int m_cmdIndex;
    int m_cmdOperType;//命令操作类型
    QTcpSocket *m_tcpSocket;

};

#endif // COMMANDABSTRACT_H

commandabstract .cpp

#include "commandabstract.h"
#include <QTcpSocket>
CommandAbstract::CommandAbstract(QObject *parent) :
    QObject(parent),
    m_cmdIndex(0),
    m_cmdOperType(0),
    m_tcpSocket(NULL)
{
}
QByteArray CommandAbstract::getSendingData() const
{
    return m_sendingData;
}
void CommandAbstract::setSendingData(const QByteArray &data)
{
    m_sendingData = data;
}
void CommandAbstract::exec()
{
    if(m_tcpSocket == NULL)
        return;
    m_tcpSocket->write(m_sendingData);
}
void CommandAbstract::setTcpSocket(QTcpSocket *tcpSocket)
{
    m_tcpSocket = tcpSocket;
    connect(m_tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
}
void CommandAbstract::setCmdIndex(int index)
{
    m_cmdIndex = index;
}
void CommandAbstract::setOperType(int operType)
{
    m_cmdOperType = operType;
}
int CommandAbstract::getCmdIndex() const
{
    return m_cmdIndex;
}
int CommandAbstract::getCmdOperType() const
{
    return m_cmdOperType;
}
void CommandAbstract::onReadyRead()
{
    QByteArray readData =  m_tcpSocket->readAll();
    parseResponseData(readData);
}

命令子类

这里DemoCommand为命令子类,实际中可以根据需要派生出许多这样的子类向服务器发送不同的命令请求。DemoCommand类实现了基类中的两个接口函数。本例中,DemoCommand命令把数据key1和key2发送给服务程序,服务程序又将数据原样返回到客户端。

democommand.h

#ifndef DEMOCOMMAND_H
#define DEMOCOMMAND_H
#include <QObject>
#include "commandabstract.h"
class DemoCommand : public CommandAbstract
{
    Q_OBJECT
public:
    explicit DemoCommand(QObject *parent = 0);
    virtual void prepareSendingData(const QVariantHash& cmdArgs);
    virtual void parseResponseData(const QByteArray& data);
};
#endif // DEMOCOMMAND_H

democommand.cpp

#include "democommand.h"
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
#include <QVariantHash>
DemoCommand::DemoCommand(QObject *parent):CommandAbstract(parent)
{
}
//把发送给服务器的数据key1和key2以json格式设置到命令基类中,共命令执行函数exec()使用
void DemoCommand::prepareSendingData(const QVariantHash &cmdArgs)
{
    QString data1 = cmdArgs.value("key1").toString();
    QString data2 = cmdArgs.value("key2").toString();
    QJsonObject jsonObj;
    jsonObj.insert("key1",data1);
    jsonObj.insert("key2",data2);
    QJsonDocument jsonDoc(jsonObj);
    this->setSendingData(jsonDoc.toJson());
    qDebug()<<"m_sendingData = "<<this->getSendingData();
}
//解析来自服务器的反馈数据,并通过信号发送到界面
void DemoCommand::parseResponseData(const QByteArray &data)
{
    //根据不同的操作类型,对服务器返回数据data进行解析
    qDebug()<<__LINE__<<__FUNCTION__<<"this->getCmdOperType() = "<<this->getCmdOperType();
    qDebug()<<__LINE__<<__FUNCTION__<<"Read data = "<<data;
    QVariantHash response;
    response.insert("response",data);
    emit this->infoResultData(response,QString());
    switch (this->getCmdOperType()) {
    case 1:{

    }break;
    case 2:{

    }break;
    case 3:{

    }break;
    default:
        break;
    }
    this->deleteLater();
}

使用方法 

void MainWindow::on_pushButtonSendCmd_clicked()
{
    if(!m_tcpSocked->isOpen()){
        m_tcpSocked->connectToHost("127.0.0.1",9090);
        m_tcpSocked->waitForConnected();
}
    QVariantHash cmdArgs;
    cmdArgs.insert("key1","data1");
    cmdArgs.insert("key2","data2");//data1和data2是模拟要发送给服务器的命令数据
    CommandAbstract* cmd = new DemoCommand();
  connect(cmd,SIGNAL(infoResultData(QVariantHash&,QString&)),this,SLOT(onInfoResultData(QVariantHash&,QString&)));
    cmd->setCmdIndex(1);
    cmd->setOperType(1);
    cmd->prepareSendingData(cmdArgs);
    cmd->setTcpSocket(m_tcpSocked);
    cmd->exec();
}
//接收来自服务器反馈的数据
void MainWindow::onInfoResultData(QVariantHash &parsedResponseData, QString &errorString)
{
    QString dataFromServer = parsedResponseData.value("response").toString();
    ui->textEdit->append(dataFromServer);
}

运行效果 

客户程序.png

服务程序.png

本文福利, 免费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值