Qt網(wǎng)絡(luò)編程的命令模式:把網(wǎng)絡(luò)命令封裝成類(lèi)

命令模式是編程設(shè)計(jì)模式中的一種耻台,這里介紹命令模式在Qt網(wǎng)絡(luò)編程中的使用,講述如何實(shí)現(xiàn)“一個(gè)類(lèi)就是一個(gè)命令”的設(shè)計(jì)思想。

基本思想

  1. 把向服務(wù)器發(fā)起的請(qǐng)求抽象為一個(gè)C++類(lèi)作烟,相似的請(qǐng)求可以封裝為同一個(gè)類(lèi)部翘,通過(guò)操作類(lèi)型來(lái)區(qū)分。(命令的操作類(lèi)型在服務(wù)器端需用科平,在客戶(hù)端解析服務(wù)器反饋的數(shù)據(jù)時(shí)也需用。)淤击;

  2. 每增加一個(gè)網(wǎng)絡(luò)命令匠抗,就增加一個(gè)C++類(lèi)故源,這些命令類(lèi)有共同的基類(lèi)污抬;

  3. 在命令基類(lèi)里聲明了接口函數(shù)(C++中為純虛函數(shù))供每個(gè)命令子類(lèi)獨(dú)立實(shí)現(xiàn);

  4. 當(dāng)使用命令時(shí)绳军,只需要構(gòu)造出命令子類(lèi)(等號(hào)左邊為基類(lèi)指針變量印机,等號(hào)右邊通過(guò)new關(guān)鍵字創(chuàng)建命令子類(lèi)),設(shè)置好命令執(zhí)行需要的相關(guān)參數(shù)门驾,最后調(diào)用命令執(zhí)行函數(shù)exec()即可向服務(wù)器發(fā)送數(shù)據(jù)射赛。


    類(lèi)圖.png

命令基類(lèi)

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);
    //接口:準(zhǔn)備要發(fā)送給服務(wù)器的數(shù)據(jù)
    //參數(shù):cmdArgs用于傳遞要發(fā)送給服務(wù)器的數(shù)據(jù)(或叫做命令參數(shù))
    //說(shuō)明:在命令子類(lèi)中實(shí)現(xiàn)該接口,并將命令參數(shù)保存到m_sendingData成員變量中
    //(m_sendingData為最終發(fā)送給服務(wù)器的數(shù)據(jù))
    virtual void prepareSendingData(const QVariantHash& cmdArgs) = 0;
    //接口:解析來(lái)自服務(wù)器的響應(yīng)數(shù)據(jù)奶是,保存到m_parsedResponseData成員變量中
    //參數(shù):data為從服務(wù)器讀取到的響應(yīng)數(shù)據(jù)
    //說(shuō)明:在命令子類(lèi)中實(shí)現(xiàn)該接口楣责,每個(gè)網(wǎng)絡(luò)命令都有不同的解析方式竣灌,因此這里抽象為接口
    virtual void parseResponseData(const QByteArray& data) = 0;
    //獲取要發(fā)送給服務(wù)器的數(shù)據(jù)m_sendingData
    virtual QByteArray getSendingData() const;
    //設(shè)置要發(fā)送給服務(wù)器的數(shù)據(jù)m_sendingData
    virtual void setSendingData(const QByteArray& data);
    //執(zhí)行命令,即:向服務(wù)器發(fā)送數(shù)據(jù)m_sendingData
    void exec();
    //設(shè)置QTcpSocket指針到命令類(lèi)中秆麸,以實(shí)現(xiàn)網(wǎng)絡(luò)通信功能
    void setTcpSocket(QTcpSocket* tcpSocket);
    //設(shè)置命令序列初嘹,記錄命令編號(hào),根據(jù)需要設(shè)置
    void setCmdIndex(int index);
    //設(shè)置命令操作類(lèi)型(當(dāng)相同的命令子類(lèi)需要實(shí)現(xiàn)不同的功能時(shí)使用沮趣,如:當(dāng)要使用相同的
    //命令子類(lèi)發(fā)送不同的數(shù)據(jù)到服務(wù)器時(shí)屯烦,通過(guò)操作類(lèi)型來(lái)區(qū)分服務(wù)器響應(yīng)的數(shù)據(jù))
    void setOperType(int operType);
protected:
    int getCmdIndex() const;
    //獲取命令操作類(lèi)型
    int getCmdOperType() const;
signals:
    //通過(guò)信號(hào)對(duì)外通知命令執(zhí)行數(shù)據(jù)m_parsedResponseData
    void infoResultData(QVariantHash& parsedResponseData,QString& errorString);
public slots:
    //當(dāng)接收到網(wǎng)絡(luò)數(shù)據(jù)響應(yīng)時(shí)執(zhí)行該槽函數(shù)
    void onReadyRead();
private:
    QVariantHash m_cmdData;//命令參數(shù)
    QByteArray m_sendingData;//最終發(fā)送給服務(wù)器的命令數(shù)據(jù)
    QVariantHash m_parsedResponseData;//解析之后的服務(wù)器響應(yīng)數(shù)據(jù)
    int m_cmdIndex;
    int m_cmdOperType;//命令操作類(lèi)型
    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);
}

命令子類(lèi)

這里DemoCommand為命令子類(lèi),實(shí)際中可以根據(jù)需要派生出許多這樣的子類(lèi)向服務(wù)器發(fā)送不同的命令請(qǐng)求房铭。DemoCommand類(lèi)實(shí)現(xiàn)了基類(lèi)中的兩個(gè)接口函數(shù)驻龟。本例中,DemoCommand命令把數(shù)據(jù)key1和key2發(fā)送給服務(wù)程序缸匪,服務(wù)程序又將數(shù)據(jù)原樣返回到客戶(hù)端翁狐。
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)
{
}
//把發(fā)送給服務(wù)器的數(shù)據(jù)key1和key2以json格式設(shè)置到命令基類(lèi)中,共命令執(zhí)行函數(shù)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();
}
//解析來(lái)自服務(wù)器的反饋數(shù)據(jù)凌蔬,并通過(guò)信號(hào)發(fā)送到界面
void DemoCommand::parseResponseData(const QByteArray &data)
{
    //根據(jù)不同的操作類(lèi)型谴蔑,對(duì)服務(wù)器返回?cái)?shù)據(jù)data進(jìn)行解析
    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是模擬要發(fā)送給服務(wù)器的命令數(shù)據(jù)
    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();
}
//接收來(lái)自服務(wù)器反饋的數(shù)據(jù)
void MainWindow::onInfoResultData(QVariantHash &parsedResponseData, QString &errorString)
{
    QString dataFromServer = parsedResponseData.value("response").toString();
    ui->textEdit->append(dataFromServer);
}

運(yùn)行效果

客戶(hù)程序.png

服務(wù)程序.png

源代碼下載

百度網(wǎng)盤(pán)分享地址:
鏈接:https://pan.baidu.com/s/1O36ltEmhY_ptAlTu-MpJ4w
提取碼:et2w
為了方便示例程序的運(yùn)行,專(zhuān)門(mén)附帶了服務(wù)器端的命令行可執(zhí)行程序commandmodeserver.exe龟梦,本例中的客戶(hù)端程序編譯運(yùn)行之后隐锭,會(huì)與它連接,它會(huì)反饋客戶(hù)端發(fā)送的數(shù)據(jù)计贰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钦睡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子躁倒,更是在濱河造成了極大的恐慌荞怒,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秧秉,死亡現(xiàn)場(chǎng)離奇詭異褐桌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)象迎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)荧嵌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人砾淌,你說(shuō)我怎么就攤上這事啦撮。” “怎么了汪厨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵赃春,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我劫乱,道長(zhǎng)织中,這世上最難降的妖魔是什么锥涕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮狭吼,結(jié)果婚禮上站楚,老公的妹妹穿的比我還像新娘。我一直安慰自己搏嗡,他們只是感情好窿春,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著采盒,像睡著了一般旧乞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磅氨,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天尺栖,我揣著相機(jī)與錄音,去河邊找鬼烦租。 笑死延赌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叉橱。 我是一名探鬼主播挫以,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窃祝!你這毒婦竟也來(lái)了掐松?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粪小,失蹤者是張志新(化名)和其女友劉穎大磺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體探膊,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杠愧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逞壁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片流济。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖猾担,靈堂內(nèi)的尸體忽然破棺而出袭灯,到底是詐尸還是另有隱情,我是刑警寧澤绑嘹,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站橘茉,受9級(jí)特大地震影響工腋,放射性物質(zhì)發(fā)生泄漏姨丈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一擅腰、第九天 我趴在偏房一處隱蔽的房頂上張望蟋恬。 院中可真熱鬧,春花似錦趁冈、人聲如沸歼争。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沐绒。三九已至,卻和暖如春旺坠,著一層夾襖步出監(jiān)牢的瞬間乔遮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工取刃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹋肮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓璧疗,卻偏偏與公主長(zhǎng)得像坯辩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崩侠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容