美文网首页
Qt多线程中基于队列实现网络通信

Qt多线程中基于队列实现网络通信

作者: Qt工坊 | 来源:发表于2018-11-20 15:53 被阅读0次

基本思想

  1. 在线程中创建QTcpSocket对象实例;
  2. 在线程类中采用队列的方式动态添加需要执行的网络命令类实例;
  3. 为命令执行线程定义空闲、数据发送和数据读取三个状态;
  4. 将网络反馈数据以信号的方式发送给命令类进行解析;

命令执行线程类

tcpcmdexecthread.h

#ifndef CMDEXECTHREADABSTRACT_H
#define CMDEXECTHREADABSTRACT_H

#include <QThread>
#include <QTcpSocket>
#include <QMutex>
class CommandAbstract;
class TcpCmdExecThread : public QThread
{
    Q_OBJECT
public:
    enum State{    //命令执行状态
        E_Idle = 0,//线程空闲
        E_Sending, //数据发送
        E_Reading  //数据读取
    };
    TcpCmdExecThread(QObject *parent = 0);
    ~TcpCmdExecThread();
    void run();
    //追加要执行的网络命令到线程的执行队列中
    void appendCmd(CommandAbstract* cmd);
signals:
    //对外通知网络反馈数据
    void response(const QByteArray& data);
public slots:
    void onSocketError(QAbstractSocket::SocketError error);
private:
    //准备下一个命令,如果成功则返回true,失败则返回false
    bool prepareNextCmd();
private:
    QList<CommandAbstract*> m_cmdQueue;//命令执行队列
    CommandAbstract* m_currentCmd;//当前命令
    QTcpSocket* m_tcpSocket;
    State m_state;//命令执行线程的状态
    QMutex m_mutex;
};
#endif // CMDEXECTHREADABSTRACT_H

tcpcmdexecthread.cpp

#include "tcpcmdexecthread.h"
#include "commandabstract.h"
#include <QDebug>
#include <QMutexLocker>
TcpCmdExecThread::TcpCmdExecThread(QObject *parent):
    QThread(parent),
    m_currentCmd(NULL),
    m_tcpSocket(NULL),
    m_state(E_Idle)
{

}

TcpCmdExecThread::~TcpCmdExecThread()
{

}

void TcpCmdExecThread::appendCmd(CommandAbstract *cmd)
{
    QMutexLocker locker(&m_mutex);
    m_cmdQueue.append(cmd);
}

void TcpCmdExecThread::run()
{
    if(m_tcpSocket)
        m_tcpSocket->deleteLater();

    m_tcpSocket = new QTcpSocket;
    connect(m_tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
            this,SLOT(onSocketError(QAbstractSocket::SocketError)));
    QString host = QString("127.0.0.1");
    int port = 9090;
    m_tcpSocket->connectToHost(host,port);
    if(!m_tcpSocket->waitForConnected())
    {
        return ;
    }
    qDebug()<<__LINE__<<__FUNCTION__<<"Tcp connected.";
    while(!isInterruptionRequested())//调用requestInterruption()可退出线程
    {
        QThread::msleep(25);
        switch(m_state)
        {
        case E_Idle:
        {
            QThread::msleep(10);
            if(m_cmdQueue.length() > 0)
            {
                bool success = prepareNextCmd();
                if(success)
                    m_state =  E_Sending;
            }
        }break;
        case E_Sending:
        {
            qDebug()<<__LINE__<<__FUNCTION__<<"m_state =  E_Sending";
            QByteArray sendingData = m_currentCmd->getSendingData();
            m_tcpSocket->write(sendingData);
            qDebug()<<__LINE__<<__FUNCTION__<<"sendingData = "<<sendingData;
            m_tcpSocket->waitForReadyRead();
            m_state =  E_Reading;

        }break;
        case E_Reading:
        {
            qDebug()<<__LINE__<<__FUNCTION__<<"m_state =  E_Reading";
            QByteArray readData = m_tcpSocket->readAll();
            //信号response采用BlockingQueuedConnection方式与命令槽函数parseResponseData连接,
            //则当parseResponseData函数执行完毕之后,线程才会继续往后执行
            emit response(readData);
            m_currentCmd->deleteLater();
            m_state =  E_Idle;
        }break;
        default:{

        }
        }
    }

}

void TcpCmdExecThread::onSocketError(QAbstractSocket::SocketError error)
{
    qDebug()<<__LINE__<<__FUNCTION__<<"error:"<<(int)error;
}

bool TcpCmdExecThread::prepareNextCmd()
{
    if(m_cmdQueue.length() > 0){
        m_currentCmd = m_cmdQueue.takeFirst();
        connect(this,SIGNAL(response(QByteArray)),
                m_currentCmd,SLOT(parseResponseData(QByteArray)),Qt::BlockingQueuedConnection);
        return true;
    }
    return false;
}

网络命令基类

commandabstract.h

#ifndef COMMANDABSTRACT_H
#define COMMANDABSTRACT_H

#include <QObject>
#include <QVariantHash>

class CommandAbstract : public QObject
{
    Q_OBJECT
public:
    explicit CommandAbstract(QObject *parent = 0);
    //接口:把命令参数组织成要发送给服务器的数据m_sendingData,其中包含操作类型cmdOperType
    virtual void prepareSendingData(const QVariantHash& cmdArgs) = 0;
    //获取要发送给服务器的数据m_sendingData
    virtual QByteArray getSendingData() const;
    virtual void setSendingData(const QByteArray& data);
    void setCmdIndex(int index);
    void setOperType(int operType);
signals:
    //通过信号对外通知命令执行数据m_parsedResponseData
    void infoResultData(QVariantHash& parsedResponseData);
public slots:
    //接口:解析来自服务器的响应数据,保存到m_parsedResponseData中
    virtual void parseResponseData(const QByteArray& data) = 0;
protected:
    int getCmdIndex() const;
    int getCmdOperType() const;
private:
    QVariantHash m_cmdData;//命令参数
    QByteArray m_sendingData;//最终发送给服务器的命令数据
    QVariantHash m_parsedResponseData;//解析之后的服务器响应数据
    int m_cmdIndex;
    int m_cmdOperType;
};
Q_DECLARE_METATYPE(CommandAbstract*)
#endif // COMMANDABSTRACT_H

commandabstract.cpp

#include "commandabstract.h"
CommandAbstract::CommandAbstract(QObject *parent) :
    QObject(parent),
    m_cmdIndex(0),
    m_cmdOperType(0)
{

}

QByteArray CommandAbstract::getSendingData() const
{
    return m_sendingData;
}

void CommandAbstract::setSendingData(const QByteArray &data)
{
    m_sendingData = data;
}

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 MainWindow::on_pushButtonSendCmd_clicked()
{
    m_tcpCmdExecThread->start();
    QVariantHash cmdArgs;
    cmdArgs.insert("key1","cmd1 data1");
    cmdArgs.insert("key2","cmd1 data2");//data1和data2是模拟要发送给服务器的命令数据

    CommandAbstract* cmd1 = new DemoCommand();//向服务程序发送的第一个命令
    //命令对象cmd解析完成之后,发送信号将数据显示到界面
    connect(cmd1,SIGNAL(infoResultData(QVariantHash&)),
            this,SLOT(onInfoResultData(QVariantHash&)));
    cmd1->setCmdIndex(1);
    cmd1->setOperType(1);
    //准备发送数据
    cmd1->prepareSendingData(cmdArgs);
    //追加命令到线程的命令队列中
    m_tcpCmdExecThread->appendCmd(cmd1);


    cmdArgs.insert("key1","cmd2 data1");
    cmdArgs.insert("key2","cmd2 data2");
    CommandAbstract* cmd2 = new DemoCommand();//向服务程序发送的第二个命令
    //命令对象cmd解析完成之后,发送信号将数据显示到界面
    connect(cmd2,SIGNAL(infoResultData(QVariantHash&)),
            this,SLOT(onInfoResultData(QVariantHash&)));
    cmd2->setCmdIndex(1);
    cmd2->setOperType(1);
    //准备发送数据
    cmd2->prepareSendingData(cmdArgs);
    //追加命令到线程的命令队列中
    m_tcpCmdExecThread->appendCmd(cmd2);
}

//显示解析后的网络数据到界面
void MainWindow::onInfoResultData(QVariantHash &parsedResponseData)
{
    QString dataFromServer = parsedResponseData.value("response").toString();
    ui->textEdit->append(dataFromServer);
}

运行效果

客户程序.png
服务程序.png

源代码下载

说明:源代码中的commandmodeserver.exe为本例中的服务端程序,本例中的客户程序会与它进行连接,提供服务端数据响应。
百度网盘分享地址:
链接:https://pan.baidu.com/s/1K3c_OJqfHsXpRBSiinMqkA
提取码:z8ix

相关文章

  • Qt多线程中基于队列实现网络通信

    基本思想 在线程中创建QTcpSocket对象实例; 在线程类中采用队列的方式动态添加需要执行的网络命令类实例; ...

  • QTcpSocket

    注意点 QTcpSocket是Qt框架中的网络通信类,它使用QSocketNotifier实现异步通信。在Qt中,...

  • 队列

    基于数组的循环队列 Java实现 基于链表的队列实现 Java实现

  • 手敲数据结构——基于最大堆实现优先队列

    这里实现的优先队列是基于最大堆实现的,java系统是基于最小堆实现的。 队列接口 优先队列实现 LeetCode上...

  • python多线程的使用

    python多线程的使用 后续还会更新其他方法实现的多线程。 Queue实现多线程 通过队列实现多线程 启动一定数...

  • 队列和线程

    [iOS多线程中,队列和执行的排列组合结果分析] 多线程中的队列有:串行队列,并发队列,全局队列,主队列。 执行的...

  • 基于Rabbitmq实现延迟队列

    转自 基于Rabbitmq实现延迟队列 基于Rabbitmq实现延迟队列 延迟队列的使用场景 淘宝订单业务:下单后...

  • 多线程队列

    0x0 队列在多线程中的应用消息产生及消费一般在不同的线程,多线程需互斥访问队列。 0x1 代码实现一个队列负责消...

  • 用线程队列解决多线程并行问题

    多线程并发,可能会造成死锁问题,因此,我们可将多线程操作放入队列中。以下,是我实现的一个简单线程队列操作类。 //...

  • 从多线程角度看SDWebImage

    目录 简介 适合人群 SDWebImage中的多线程设计到的多线程实现技术主要线程/队列数据保护方案 妙处与启发 ...

网友评论

      本文标题:Qt多线程中基于队列实现网络通信

      本文链接:https://www.haomeiwen.com/subject/lqqifqtx.html