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
    评论
m_pDbProcPic = new CDBProcess("sqlserver"); m_pDbProc->openDB("mysvr", "mydb", "myusername", "mypwd"); m_pDbProcPic = new CDBProcess("mysql"); m_pDbProc->openDB("localhost", "mydb", "root", "password"); m_pDbProcPic = new CDBProcess("access"); m_pDbProc->openDB("", strMDB, "sa", "password"); m_pDbProcPic = new CDBProcess("sqlite"); m_pDbProcPic->openDB("", "mysqlitedb"); CDBProcess使用说明 构造函数: CDBProcess(const QString strType); 参数:为数据库类型,不区分大小写,支持的类型有 sqlite mysql access sqlserver 例: CDBProcess db("sqlite"); -------------------------------------------------- 打开数据库 bool openDB(const QString strSvrName, //服务器名 const QString strDBname, //数据库名 const QString strUserID="", //用户名 const QString strUserPwd=""); //密码 打开数据库成功,返回true,否则返回false 对于sqlite,只有strDBname有效,其它参数忽略,如db.openDB("", "mysqlite.db") 对于MS Access数据库,strSvrName为空,用户名默认为"sa",如db.openDB("", "myaccess.mdb"); 对MSAccess数据库,也可通过一个UDL文件,打开,如db.openDB("my.udl", ""); mysql和sqlserver,就是各个参数依次提供,如db.openDB("svr1", "mydb", "user1", "abcd"); ----------------------------------------------------- 关闭数据库,CDBProcess析构时,亦会自动关闭数据库 void closeDB(); //db.closeDB(); ------------------------------------------------------ 执行Sql语句 bool excuteSQL(const QString); ------------------------------------------------------- 打开记录集 bool openRecordsetBySql(const QString strSql, //Sql语句 int idx = -1); //记录集id,用于标识记录集,默认值为-1 例: db.openRecordsetBySql("SELECT * FROM TB1", 5); 打开一个记录集,标识号为5,后面操作这个记录集,也要提供这个标识号 -------------------------------------------------------- 关闭记录集 void closeRecordset(int idx = -1); 例: db.closeRecordset(5); 关闭之前打开的标识为5的记录集 ----------------------------------- 数据库是否处在打开状态 bool isOpen() const; ------------------------------------ 记录集游标是否在结尾,参数为记录集标识 bool recEOF(int idx = -1) const; 例:bool b = db.RecBOF(5); ------------------------------------ 记录集游标是否在第一条记录之前,参数为记录集标识 bool recBOF(int idx = -1) const; ---------------------------------------- 删除一个表 bool dropTable(const QString); --------------------------------------------- 读取标识为idx记录集的当前记录的各字段值,方法如示例: bool getFieldsValueFromRec(int idx, const char* szFldInfo, ...) const; int iSN; QString strName; double dHeight; QDateTime dt; QByteArray ba; db.getFieldsValueFromRec(5, //记录集id "sn%d", &iSN, //字段名加类型 sn为字段名%d表示整型,&iSN传入指针,保存返回字段值 "name%s", &strName, //字段名加类型 name为字段名%s表示字符串(QString) "height&f", &dHeight, //字段名加类型 height为字段名%f表示小数(double) "birthday%t", &dt, //字段名加类型 birthday为字段名%t表示时间(QDateTime) "photo%b", &ba, //字段名加类型 photo为字段名%b表示二进制流(QByteArray) CDBProcess::szEnd); //结束标志,"|" 执行后,各字段值就保存在iSN, strName等变量中了。 参数:第一个参数为记录集id 后面可变参数,字段%类型标识,用于保存返回值的指针, 类型标识:%d-int %s-QString %f-double %t-QDateTime %b-QByteArray --------------------------------------------------------------------------- 向一个数据表中填加一条记录 bool addFieldsValueToTbl(const QString strTbl, const char* szFldInfo, ...); 参数:第一个参数strTbl,为表名 后面是可变参数,为"字段%类型标识",值(或指针),注int,double类型直接传值,其它传指针 例: db.addFieldsValueToTbl("TB1", //表名 "sn%d", iSN, //字段名加类型 sn为字段名%d表示整型,iSN传入值 "name%s", &strName, //字段名加类型 name为字段名%s表示字符串(QString), 传入QString变量指针 "height&f", dHeight, //字段名加类型 height为字段名%f表示小数(double),dHeight传入值 "birthday%t", &dt, //字段名加类型 birthday为字段名%t表示时间(QDateTime),传入指针 "photo%b", &ba, //字段名加类型 photo为字段名%b表示二进制流(QByteArray),传入指针 CDBProcess::szEnd); //结束标志,"|" ----------------------------------------------------------- 修改表中符合WHERE子句条件的记录 bool updateTblFieldsValue(const QString strTbl, QString strWhere, const char * szFldInfo, ... ); strTbl表名 strWhere SQL WHERE子句,如"WHERE sn=20" const char * szFldInfo, ...可变参数,同addFieldsValueToTbl 例: db.updateTblFieldsValue("TB1", "WHERE sn=20", "height&f", dHeight, "photo%b", &ba, CDBProcess::szEnd); ---------------------------------------------------------------- 以下几个函数分别是获取记录数,和记录光标移动。参数为记录集标识 long getRecordCount(int idx = -1) const; bool moveFirst(int idx = -1) const; bool movePrevious(int idx = -1) const; bool moveNext(int idx = -1) const; bool moveLast(int idx = -1) const; bool moveTo(int n, int idx = -1) const; -------------------------------------------------------------------- 返回数据库名 QString getDbName() const; ------------------------ 以下几个函数未验证 bool execStoreProcOfArgList(int idx, const QString strStoreProc, const char* szFldsInfo, ...); bool exexProc(const QString strStoreProc, QString str1, QString& str2); bool transaction(); bool commit(); bool rollback();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值