这篇文章给大家分享的是有关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服务端多线程的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/3936639/blog/4498068