Qt服务端多线程的示例分析

发布时间:2021-12-03 13:44:42 作者:小新
来源:亿速云 阅读:140

这篇文章给大家分享的是有关Qt服务端多线程的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

 该例子仅使用两个线程, 一个线程负责监听新的连接,一个线程用来处理已经建立连接的客户端事件(此处可以用一个线程池来提高性能)。消息接收加入了一一个简单分包机制,每条消息的前四个字节存储的是 uint32_t 类型,指该条消息整个长度, 这样就可以很好区分出每个消息。该代码在许多细节上有些不严谨的地方,仅供从参考

// tcpserver.h

class CClientSocket;

class CTcpServer final : public QTcpServer
{
    Q_OBJECT
public:
    explicit CTcpServer(QObject *parent = nullptr);
    virtual ~CTcpServer() override;

    void Listen(int _iPort);
    void Nortify(const QByteArray &_csMessage);

protected:
    virtual void incomingConnection(qintptr socketDescriptor) override;
    virtual void timerEvent(QTimerEvent *event) override;

public slots:
    void SLOT_ClientDisconnect();

private:
    void _PackageMessage(QByteArray &_baMsg);

private:
    QList<std::shared_ptr<CClientSocket>> m_lstSocket;     ///< 连接的客户端
    QThread * m_pEventThd;  ///< 事件线程
};
// tcpserver.cpp
CTcpServer::CTcpServer(QObject *parent) : QTcpServer(parent),
    m_pEventThd(new QThread())
{
    m_lstSocket.clear();
}

CTcpServer::~CTcpServer()
{

}

void CTcpServer::Listen(int _iPort)
{
    this->listen(QHostAddress::Any, static_cast<quint16>(_iPort));
    m_pEventThd->start();
    QObject::startTimer(5 * 1000);
}

void CTcpServer::Nortify(const QByteArray &_csMessage)
{
    QByteArray baSendMsg = _csMessage;
    _PackageMessage(baSendMsg);
    for (auto pClientSocket : m_lstSocket)
    {
        pClientSocket->SendMsg(baSendMsg);
    }

    QThread::msleep(50);
}

void CTcpServer::incomingConnection(qintptr socketDescriptor)
{
    qDebug() << "#################MainThread:" << QThread::currentThread() << m_pEventThd;
    std::shared_ptr<CClientSocket> pClient = std::make_shared<CClientSocket>(socketDescriptor, nullptr);
    connect(pClient.get(), &CClientSocket::SIGNAL_Disconneted, this, &CTcpServer::SLOT_ClientDisconnect);
    pClient->InitSocket(m_pEventThd);
    m_lstSocket.push_back(pClient);
    emit newConnection();
}

void CTcpServer::timerEvent(QTimerEvent *event)
{
    this->Nortify("hello world");
}

void CTcpServer::SLOT_ClientDisconnect()
{
    CClientSocket *pClient = dynamic_cast<CClientSocket*>(QObject::sender());
    if (pClient)
    {
        for (const auto &index : m_lstSocket)
        {
            if (index.get() == pClient)
            {
                m_lstSocket.removeOne(index);
                return;
            }
        }
    }
}

void CTcpServer::_PackageMessage(QByteArray &_baMsg)
{
    uint32_t iSize = static_cast<uint32_t>(_baMsg.size());
    iSize = ::ntohl(iSize);
    _baMsg.prepend(reinterpret_cast<char*>(&iSize), sizeof (iSize));
}
// clientsocket
class CClientSocket : public QTcpSocket
{
    Q_OBJECT
private:
    struct TMsgCache
    {
        void Clear()
        {
            iSize = 0;
            baPacket = "";
        }

        size_t iSize = 0;  ///< 包的实际长度 去除包头长度
        QByteArray baPacket = ""; ///< 原始字段
    };

public:
    explicit CClientSocket(int _iFd, QObject *parent = nullptr);
    virtual ~CClientSocket() override;
    void InitSocket(QThread * _pThread);
    void SendMsg(const QByteArray &_baMessage);

protected:
    virtual void timerEvent(QTimerEvent *event) override;

private:
    void _DeInitSocket();
    void _UpdateHeartTime();

private:
    Q_INVOKABLE void _StartCheckTimer();
    Q_INVOKABLE void _SendMessage(const QByteArray &_baMessage);

private slots:
    void SLOT_ReadyRead();
    void SLOT_SocketError(QAbstractSocket::SocketError _eError);
    void SLOT_Disconnect();

signals:
    void SIGNAL_Disconneted();

private:
    QString m_sCabinetCode;     ///< 柜体编号
    qint64 m_iOldResponseTimeStamp;     ///< 上一次响应的时间戳
    int m_iTimeId;  ///< 心跳包检测时间
    TMsgCache m_tMsgCache;  ///< 消息缓存结构体
};
// clientsoket.cpp
#define READ_MAX_SIZE 1024

CClientSocket::CClientSocket(int _iFd, QObject *parent) : QTcpSocket(parent)
{
    this->setSocketDescriptor(_iFd);
    connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this,
            SLOT(SLOT_SocketError(QAbstractSocket::SocketError)));
    connect(this, &QTcpSocket::readyRead, this, &CClientSocket::SLOT_ReadyRead);
    connect(this, &QTcpSocket::disconnected, this, &CClientSocket::SLOT_Disconnect);
    m_iOldResponseTimeStamp = QDateTime::currentDateTime().toSecsSinceEpoch();
}

CClientSocket::~CClientSocket()
{
    this->close();
    qDebug() << "###################CClientSocket destruct";
}

void CClientSocket::InitSocket(QThread *_pThread)
{
    this->moveToThread(_pThread);
    QMetaObject::invokeMethod(this, &CClientSocket::_StartCheckTimer);
}

void CClientSocket::SendMsg(const QByteArray &_baMessage)
{
    QMetaObject::invokeMethod(this, "_SendMessage", Q_ARG(const QByteArray&, _baMessage));
}

void CClientSocket::timerEvent(QTimerEvent *event)
{
    if (m_iTimeId == event->timerId())
    {
       if (abs(QDateTime::currentSecsSinceEpoch() - m_iOldResponseTimeStamp) > 60)
       {
            this->disconnectFromHost();
       }
    }
}

void CClientSocket::_DeInitSocket()
{
    this->close();
    QObject::killTimer(m_iTimeId);
    m_iTimeId = 0;
}

void CClientSocket::_UpdateHeartTime()
{
    m_iOldResponseTimeStamp = QDateTime::currentDateTime().toSecsSinceEpoch();
}

void CClientSocket::_StartCheckTimer()
{
    qDebug() << "##########################_StartCheckTimer";
    m_iTimeId = QObject::startTimer(1000 * 5);
}

void CClientSocket::_SendMessage(const QByteArray &_baMessage)
{
    if (this->isWritable())
    {
        this->write(_baMessage);
        this->flush();
    }
}

void CClientSocket::SLOT_ReadyRead()
{
    char cBuffer[READ_MAX_SIZE];
    qint64 iReadSize = 0;
    QByteArray baNewCache;
    do{
        iReadSize = this->read(cBuffer, READ_MAX_SIZE);
        if (iReadSize == -1) ///< 网络异常
        {
            qDebug() << QString("############################Read Socket Error, %1:%2").arg(this->peerAddress().toString())
                        .arg(this->peerPort());
            this->SLOT_Disconnect();
            return;
        }

        if (iReadSize != 0)
        {
            baNewCache.append(cBuffer, static_cast<int>(iReadSize));
        }
    }while (iReadSize != 0);

    if (m_tMsgCache.baPacket.size() != 0)
    {
        baNewCache = m_tMsgCache.baPacket + baNewCache;
    }

    while (baNewCache.size() > 0)
    {
        if (baNewCache.size() > 4)
        {
            uint32_t iSize;
            if (m_tMsgCache.iSize == 0)
            {
                QByteArray baSize = baNewCache.mid(0, 4);
                ::memcpy(&iSize, baSize.data(), sizeof(iSize));
                iSize = ::ntohl(iSize);
            }
            else{
                iSize = m_tMsgCache.iSize;
            }

            if (baNewCache.size() >= static_cast<int>(iSize)) // 分解出一个完整的消息包
            {
                m_tMsgCache.baPacket = baNewCache.mid(4, static_cast<int>(iSize - 4));
//                // 动作:推入到执行线程队列
//                m_pHandleMessageThd->Push(m_tMsgCache.baPacket );

                // 重置缓存状态
                m_tMsgCache.Clear();
                // 检测下一个新的消息包

                baNewCache = baNewCache.mid(static_cast<int>(iSize));
            }
            else {
                m_tMsgCache.iSize = iSize;
                m_tMsgCache.baPacket = baNewCache;
                break;
            }
        }
        else{
            // 没有完整4字节长度值
            m_tMsgCache.iSize = 0;
            m_tMsgCache.baPacket = baNewCache;
            break;
        }
    }
    this->_UpdateHeartTime();
}

void CClientSocket::SLOT_SocketError(QAbstractSocket::SocketError _eError)
{
   qDebug() << QString("CClientSocket(%1:%2)disconnet, error:%3").arg(this->peerAddress().toString())
            .arg(this->peerPort()).arg(_eError);
   _DeInitSocket();
   emit SIGNAL_Disconneted();
}

void CClientSocket::SLOT_Disconnect()
{
    qDebug() << QString("client disconnect");
    _DeInitSocket();
    emit SIGNAL_Disconneted();
}

感谢各位的阅读!关于“Qt服务端多线程的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

推荐阅读:
  1. SOCKET之多线程的示例分析
  2. QT多线程深入分析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

qt

上一篇:Qt如何实现http服务

下一篇:tk.Mybatis插入数据获取Id怎么实现

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》