神秘博客

QTcpServer服务端多线程连接

从文档了解到如果要实现多线程,必须重新实现void incomingConnection(qintptr socketDescriptor),因为默认的会把监听到的套接字创建后挂起,每次只通知一个连接,想要多线程继承QTcpServer后重新实现

//TcpServer.h
#pragma once

#include <QTcpServer>
#include <qsemaphore.h>
#include <QMap>

class TcpThread;

class TcpServer : public QTcpServer
{
	Q_OBJECT

public:
	TcpServer(QObject* parent = nullptr);
	~TcpServer();
	//退出TCP客户端线程
	void closeTcpThread(); 
	  //设置是否打开注册
	void setOpen(bool mode);
	  //返回Tcp线程数量
	int readTcpNum();
	//设置关闭模式
	void setCloseMode(bool mode);

private:
	bool open;
	QMap<qintptr, TcpThread*> tcpThread;
	int connectionNum;  //连接数
	QSemaphore *seamphore;  //线程锁
	int sqlNum;  //保存数据库连接数
	bool closeMode;  //是否关闭

protected:
	void incomingConnection(qintptr socketDescriptor);

private slots:
	//读取消息
	void readMessage(const QString &str);
	//读取刷新数据库信息
	void readSelectSql();
	//关闭连接
	void closeConnection(qintptr socket); 

signals:
	//退出线程
	void quitThread();
	//显示消息
	void showMessage(const QString &str);
	//刷新数据库
	void selectSql();
	//关闭完成
	void closeFinished();
};
//TcpServer.cpp
#include "TcpServer.h"
#include "TcpThread.h"
#include <qdebug.h>

TcpServer::TcpServer(QObject *parent)
	: QTcpServer(parent)
{
	connectionNum = 0;
	seamphore = new QSemaphore;
	seamphore->release();
	qRegisterMetaType<qintptr>("qintptr");  //注册自定义信号类型
	sqlNum = 0;
	open = false;
	closeMode = false;
}

TcpServer::~TcpServer()
{
}

//退出TCP客户端线程
void TcpServer::closeTcpThread()
{
	closeMode = true;
	if (!tcpThread.isEmpty()) {
		emit quitThread();
	}
	else {
		emit closeFinished();
	}
}

//设置是否打开注册
void TcpServer::setOpen(bool mode)
{
	open = mode;
}

//返回Tcp线程数量
int TcpServer::readTcpNum()
{
	return tcpThread.size();
}

//设置关闭模式
void TcpServer::setCloseMode(bool mode)
{
	closeMode = mode;
}


void TcpServer::incomingConnection(qintptr socketDescriptor)
{
	seamphore->acquire();  //防止并发 锁住
	tcpThread[socketDescriptor] = new TcpThread(sqlNum, socketDescriptor, this);
	tcpThread[socketDescriptor]->setOpen(open);
	connect(this, &TcpServer::quitThread, tcpThread[socketDescriptor], &TcpThread::closeTcpSocket, Qt::DirectConnection);
	connect(tcpThread[socketDescriptor], &TcpThread::closeConnection, this, &TcpServer::closeConnection);
	connect(tcpThread[socketDescriptor], &TcpThread::showMessage, this, &TcpServer::readMessage);
	connect(tcpThread[socketDescriptor], &TcpThread::refreshDatabase, this, &TcpServer::readSelectSql);
	tcpThread[socketDescriptor]->start();
	seamphore->release();
}

//读取刷新数据库信息
void TcpServer::readSelectSql()
{
	emit selectSql();
}

//关闭连接
void TcpServer::closeConnection(qintptr socket)
{
	tcpThread.remove(socket);
	if (tcpThread.isEmpty()) {
		sqlNum = 0;
		if (closeMode) {
			emit closeFinished();
		}
	}
}

//读取消息
void TcpServer::readMessage(const QString &str)
{
	emit showMessage(str);
}

下面再实现QTcpSocket多线程类,继承QThread后重写就行了,可以把读取写入数据一起放在这个线程里面

//TcpThread.h
#pragma once

#include <QThread>
#include <qtcpsocket.h>
#include <qsqlquery.h>

class TcpThread : public QThread
{
	Q_OBJECT

public:
	TcpThread(const int &s, qintptr id, QObject* parent = nullptr);
	~TcpThread();
	void run();
	int checkSignIn_1(const QString& strName, const QString& passWord, const QString& cpuid);
	int checkSignIn_2(const QString& strName, const QString& passWord, const QString& cpuid);
	int checkSignUp_1(const QString& strName, const QString& passWord, const QString& cpuid);
	int checkSignUp_2(const QString& strName, const QString& passWord, const QString& cpuid);
	//关闭TCP线程
	void closeTcpSocket();
	//设置是否打开注册
	void setOpen(bool mode);

private:
	QTcpSocket* tcpSocket;
	bool open;
	QSqlDatabase *db;
	QSqlQuery *query;
	qintptr socketDescriptor;
	QString ip;
	int sqlNum;  //保存数据库连接数

private slots:
	//读取消息
	void readMessage();
	//断开连接
	void disconnectFinish();
	//退出完成
	void quitFinished();

signals:
	//刷新数据库
	void refreshDatabase();
	//发送消息
	void showMessage(const QString &str);
	//关闭连接
	void closeConnection(qintptr socket);
};
//TcpThread.cpp
#include "TcpThread.h"
#include <QTime>
#include <qhostaddress.h>
#include <qsqldatabase.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>

TcpThread::TcpThread(const int &s, qintptr id, QObject* parent)
	: sqlNum(s), socketDescriptor(id), QThread(parent)
{
	open = false;
	db = nullptr;
	query = nullptr;
	tcpSocket = nullptr;
}

TcpThread::~TcpThread()
{
	if (db != nullptr) {
		delete db;
		delete query;
	}
	if (tcpSocket != nullptr)
		tcpSocket->deleteLater();
}

void TcpThread::run()
{
	if (db == nullptr) {
		db = new QSqlDatabase;
		if (QSqlDatabase::contains(QString("%1").arg(sqlNum)))
			QSqlDatabase::removeDatabase(QString("%1").arg(sqlNum));

		*db = QSqlDatabase::addDatabase("QSQLITE", QString("%1").arg(sqlNum));
		db->setDatabaseName("data.db");
		query = new QSqlQuery(*db);
	}

	if (tcpSocket == nullptr) {
		tcpSocket = new QTcpSocket;
		connect(tcpSocket, &QTcpSocket::readyRead, this, &TcpThread::readMessage, Qt::DirectConnection);  //如果有可读的数据 发出信号时立即调用
		connect(tcpSocket, &QTcpSocket::disconnected, this, &TcpThread::disconnectFinish);
		connect(this, &TcpThread::finished, this, &TcpThread::quitFinished);
	}

	if (!tcpSocket->setSocketDescriptor(socketDescriptor)) {
		emit showMessage(QStringLiteral("设置套接字错误 %1").arg(socketDescriptor));
		return;
	}
	ip = tcpSocket->peerAddress().toString().replace("::ffff:", "");

	this->exec();
}

//神秘京东工具登录
int TcpThread::checkSignIn_1(const QString& strName, const QString& passWord, const QString& cpuid)
{
	if (!db->isOpen())
		db->open();

	query->exec(QString("select * from accounts where name = '%1' and password = '%2';").arg(strName).arg(passWord));  //查询用户
	query->next();
	if (query->isValid()) {
		if (query->value(3).toString() >= QDateTime::currentDateTime().toString("yyyy-MM-dd")) {
			if (query->value(4).toString() == cpuid) {
				return 1;
			}
			else
				return 3;
		}
		else
			return 2;
	}

	db->close();

	return 0;
}

//JD Key登录
int TcpThread::checkSignIn_2(const QString& strName, const QString& passWord, const QString& cpuid)
{
	if (!db->isOpen())
		db->open();

	query->exec(QString("select * from keyaccounts where name = '%1' and password = '%2';").arg(strName).arg(passWord));  //查询用户
	query->next();
	if (query->isValid()) {
		if (query->value(3).toString() >= QDateTime::currentDateTime().toString("yyyy-MM-dd")) {
			if (query->value(4).toString() == cpuid) {
				return 1;
			}
			else
				return 3;
		}
		else
			return 2;
	}

	db->close();

	return 0;
}

//神秘京东工具注册
int TcpThread::checkSignUp_1(const QString& strName, const QString& passWord, const QString& cpuid)
{
	if (open)
	{
		if (!db->isOpen())
			db->open();

		query->exec(QString("select * from accounts where name = '%1';").arg(strName));  //查询用户
		query->next();
		if (query->isValid()) {
			return 0;
		}
		query->exec(QString("insert into accounts values(NULL,'%1','%2','%3','%4')").arg(strName).arg(passWord).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd")).arg(cpuid));
		
		db->close();

		emit refreshDatabase();
		return 1;
	}
	else
		return 3;
}

//JD Key注册
int TcpThread::checkSignUp_2(const QString& strName, const QString& passWord, const QString& cpuid)
{
	if (open)
	{
		if (!db->isOpen())
			db->open();

		query->exec(QString("select * from keyaccounts where name = '%1';").arg(strName));  //查询用户
		query->next();
		if (query->isValid()) {
			return 0;
		}
		query->exec(QString("insert into keyaccounts values(NULL,'%1','%2','%3','%4')").arg(strName).arg(passWord).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd")).arg(cpuid));


		db->close();

		emit refreshDatabase();
		return 1;
	}
	else
		return 3;
}

//关闭TCP线程
void TcpThread::closeTcpSocket()
{
	QMetaObject::invokeMethod(tcpSocket, std::bind(&QTcpSocket::close, tcpSocket));
}

//设置是否打开注册
void TcpThread::setOpen(bool mode)
{
	open = mode;
}

//读取连接数据
void TcpThread::readMessage() {
	QStringList dataList;
	QString data = tcpSocket->readAll();
	QByteArray allData = data.toUtf8();
	QJsonParseError error;
	QJsonDocument doc = QJsonDocument::fromJson(allData, &error);
	if (error.error == QJsonParseError::NoError) {
		QJsonObject rootObject = doc.object();

		if (rootObject.contains("Sql")) {
			QJsonValue jsonValue = rootObject.value("Sql");
			dataList << jsonValue.toString();
		}

		if (rootObject.contains("Type")) {
			QJsonValue jsonValue = rootObject.value("Type");
			dataList << jsonValue.toString();
		}

		if (rootObject.contains("User")) {
			QJsonValue jsonValue = rootObject.value("User");
			dataList << jsonValue.toString();
		}

		if (rootObject.contains("Pass")) {
			QJsonValue jsonValue = rootObject.value("Pass");
			dataList << jsonValue.toString();
		}

		if (rootObject.contains("Cpu")) {
			QJsonValue jsonValue = rootObject.value("Cpu");
			dataList << jsonValue.toString();
		}
	}

	if (dataList.size() == 5) {
		QJsonObject rootObject;

		if (dataList[0] == "0") {
			int ret;
			if (dataList[1] == "A")     //注册
				ret = checkSignUp_1(dataList[2], dataList[3], dataList[4]);
			else if (dataList[1] == "B")  //登录
				ret = checkSignIn_1(dataList[2], dataList[3], dataList[4]);
			else
				return;

			rootObject.insert("Type", dataList[1]);
			if (ret == 1)
				rootObject.insert("Ret", "true");
			else
				rootObject.insert("Ret", "false");
			rootObject.insert("Ret_Code", QString("%1").arg(ret));
		}
		else if (dataList[0] == "1") {
			int ret;
			if (dataList[1] == "A")     //注册
				ret = checkSignUp_2(dataList[2], dataList[3], dataList[4]);
			else if (dataList[1] == "B")  //登录
				ret = checkSignIn_2(dataList[2], dataList[3], dataList[4]);
			else
				return;

			rootObject.insert("Type", dataList[1]);
			if (ret == 1)
				rootObject.insert("Ret", "true");
			else
				rootObject.insert("Ret", "false");
			rootObject.insert("Ret_Code", QString("%1").arg(ret));
		}
		doc.setObject(rootObject);
		tcpSocket->write(doc.toJson());
	}
	else {
		QJsonObject rootObject;
		rootObject.insert("Ret_Code", QStringLiteral("未知指令"));
		doc.setObject(rootObject);
		tcpSocket->write(doc.toJson());
	}
}

//断开连接信号
void TcpThread::disconnectFinish()
{
	this->quit();
}

//退出完成
void TcpThread::quitFinished()
{
	emit closeConnection(socketDescriptor);
	this->deleteLater();
}
版权说明:
点赞

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

觉得文章有用就请我吃包辣条吧

微信扫一扫打赏