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


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

    基本思想

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

    类图.png

    命令基类

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

    commandabstract .h

    1. #ifndef COMMANDABSTRACT_H
    2. #define COMMANDABSTRACT_H
    3. #include
    4. #include
    5. class QTcpSocket;
    6. class CommandAbstract : public QObject
    7. {
    8. Q_OBJECT
    9. public:
    10. explicit CommandAbstract(QObject *parent = 0);
    11. //接口:准备要发送给服务器的数据
    12. //参数:cmdArgs用于传递要发送给服务器的数据(或叫做命令参数)
    13. //说明:在命令子类中实现该接口,并将命令参数保存到m_sendingData成员变量中
    14. //(m_sendingData为最终发送给服务器的数据)
    15. virtual void prepareSendingData(const QVariantHash& cmdArgs) = 0;
    16. //接口:解析来自服务器的响应数据,保存到m_parsedResponseData成员变量中
    17. //参数:data为从服务器读取到的响应数据
    18. //说明:在命令子类中实现该接口,每个网络命令都有不同的解析方式,因此这里抽象为接口
    19. virtual void parseResponseData(const QByteArray& data) = 0;
    20. //获取要发送给服务器的数据m_sendingData
    21. virtual QByteArray getSendingData() const;
    22. //设置要发送给服务器的数据m_sendingData
    23. virtual void setSendingData(const QByteArray& data);
    24. //执行命令,即:向服务器发送数据m_sendingData
    25. void exec();
    26. //设置QTcpSocket指针到命令类中,以实现网络通信功能
    27. void setTcpSocket(QTcpSocket* tcpSocket);
    28. //设置命令序列,记录命令编号,根据需要设置
    29. void setCmdIndex(int index);
    30. //设置命令操作类型(当相同的命令子类需要实现不同的功能时使用,如:当要使用相同的
    31. //命令子类发送不同的数据到服务器时,通过操作类型来区分服务器响应的数据)
    32. void setOperType(int operType);
    33. protected:
    34. int getCmdIndex() const;
    35. //获取命令操作类型
    36. int getCmdOperType() const;
    37. signals:
    38. //通过信号对外通知命令执行数据m_parsedResponseData
    39. void infoResultData(QVariantHash& parsedResponseData,QString& errorString);
    40. public slots:
    41. //当接收到网络数据响应时执行该槽函数
    42. void onReadyRead();
    43. private:
    44. QVariantHash m_cmdData;//命令参数
    45. QByteArray m_sendingData;//最终发送给服务器的命令数据
    46. QVariantHash m_parsedResponseData;//解析之后的服务器响应数据
    47. int m_cmdIndex;
    48. int m_cmdOperType;//命令操作类型
    49. QTcpSocket *m_tcpSocket;
    50. };
    51. #endif // COMMANDABSTRACT_H

    commandabstract .cpp

    1. #include "commandabstract.h"
    2. #include <QTcpSocket>
    3. CommandAbstract::CommandAbstract(QObject *parent) :
    4. QObject(parent),
    5. m_cmdIndex(0),
    6. m_cmdOperType(0),
    7. m_tcpSocket(NULL)
    8. {
    9. }
    10. QByteArray CommandAbstract::getSendingData() const
    11. {
    12. return m_sendingData;
    13. }
    14. void CommandAbstract::setSendingData(const QByteArray &data)
    15. {
    16. m_sendingData = data;
    17. }
    18. void CommandAbstract::exec()
    19. {
    20. if(m_tcpSocket == NULL)
    21. return;
    22. m_tcpSocket->write(m_sendingData);
    23. }
    24. void CommandAbstract::setTcpSocket(QTcpSocket *tcpSocket)
    25. {
    26. m_tcpSocket = tcpSocket;
    27. connect(m_tcpSocket,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
    28. }
    29. void CommandAbstract::setCmdIndex(int index)
    30. {
    31. m_cmdIndex = index;
    32. }
    33. void CommandAbstract::setOperType(int operType)
    34. {
    35. m_cmdOperType = operType;
    36. }
    37. int CommandAbstract::getCmdIndex() const
    38. {
    39. return m_cmdIndex;
    40. }
    41. int CommandAbstract::getCmdOperType() const
    42. {
    43. return m_cmdOperType;
    44. }
    45. void CommandAbstract::onReadyRead()
    46. {
    47. QByteArray readData = m_tcpSocket->readAll();
    48. parseResponseData(readData);
    49. }

    命令子类

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

    democommand.h

    1. #ifndef DEMOCOMMAND_H
    2. #define DEMOCOMMAND_H
    3. #include
    4. #include "commandabstract.h"
    5. class DemoCommand : public CommandAbstract
    6. {
    7. Q_OBJECT
    8. public:
    9. explicit DemoCommand(QObject *parent = 0);
    10. virtual void prepareSendingData(const QVariantHash& cmdArgs);
    11. virtual void parseResponseData(const QByteArray& data);
    12. };
    13. #endif // DEMOCOMMAND_H

    democommand.cpp

    1. #include "democommand.h"
    2. #include <QDebug>
    3. #include <QJsonObject>
    4. #include <QJsonDocument>
    5. #include <QVariantHash>
    6. DemoCommand::DemoCommand(QObject *parent):CommandAbstract(parent)
    7. {
    8. }
    9. //把发送给服务器的数据key1key2以json格式设置到命令基类中,共命令执行函数exec()使用
    10. void DemoCommand::prepareSendingData(const QVariantHash &cmdArgs)
    11. {
    12. QString data1 = cmdArgs.value("key1").toString();
    13. QString data2 = cmdArgs.value("key2").toString();
    14. QJsonObject jsonObj;
    15. jsonObj.insert("key1",data1);
    16. jsonObj.insert("key2",data2);
    17. QJsonDocument jsonDoc(jsonObj);
    18. this->setSendingData(jsonDoc.toJson());
    19. qDebug()<<"m_sendingData = "<<this->getSendingData();
    20. }
    21. //解析来自服务器的反馈数据,并通过信号发送到界面
    22. void DemoCommand::parseResponseData(const QByteArray &data)
    23. {
    24. //根据不同的操作类型,对服务器返回数据data进行解析
    25. qDebug()<<__LINE__<<__FUNCTION__<<"this->getCmdOperType() = "<<this->getCmdOperType();
    26. qDebug()<<__LINE__<<__FUNCTION__<<"Read data = "<<data;
    27. QVariantHash response;
    28. response.insert("response",data);
    29. emit this->infoResultData(response,QString());
    30. switch (this->getCmdOperType()) {
    31. case 1:{
    32. }break;
    33. case 2:{
    34. }break;
    35. case 3:{
    36. }break;
    37. default:
    38. break;
    39. }
    40. this->deleteLater();
    41. }

    使用方法 

    1. void MainWindow::on_pushButtonSendCmd_clicked()
    2. {
    3. if(!m_tcpSocked->isOpen()){
    4. m_tcpSocked->connectToHost("127.0.0.1",9090);
    5. m_tcpSocked->waitForConnected();
    6. }
    7. QVariantHash cmdArgs;
    8. cmdArgs.insert("key1","data1");
    9. cmdArgs.insert("key2","data2");//data1data2是模拟要发送给服务器的命令数据
    10. CommandAbstract* cmd = new DemoCommand();
    11. connect(cmd,SIGNAL(infoResultData(QVariantHash&,QString&)),this,SLOT(onInfoResultData(QVariantHash&,QString&)));
    12. cmd->setCmdIndex(1);
    13. cmd->setOperType(1);
    14. cmd->prepareSendingData(cmdArgs);
    15. cmd->setTcpSocket(m_tcpSocked);
    16. cmd->exec();
    17. }
    18. //接收来自服务器反馈的数据
    19. void MainWindow::onInfoResultData(QVariantHash &parsedResponseData, QString &errorString)
    20. {
    21. QString dataFromServer = parsedResponseData.value("response").toString();
    22. ui->textEdit->append(dataFromServer);
    23. }

    运行效果 

    客户程序.png

    服务程序.png

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

  • 相关阅读:
    1010 Radix
    我的创作纪念日
    【目标检测】使用python代码实现视频转为图片
    开源框架面试之MyBatis面试题
    SANSAN每周新鲜事|透传还能这么玩:用开源物联网平台实现设备互联
    基于单片机的胎压监测系统的设计
    【软考】数据通讯基础1
    apache html调用bash脚本案例
    十分钟带你上手egg,写自己的后端接口
    iOS 使用runtime 解决按钮重复点击的问题
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/127704065