实现功能 :
用户的注册及登录 ,个人答题训练 ,排位赛(网络对战) ,排位赛时发送表情、排位系统、积分榜。
项目描述 :
采用 C/S 结构基于 TCP/IP 协议 ,运用 Libevent 库进行服务器端搭建 ,epoll+线程池实现多路复用。 使用 JSON 进行轻量级的数据格式转换 ,Spdlog 进行对各个环节过程的记录。在服务器端通过 Libevent 对客户 端发来的信息通过事件区分进行分块处理(读 ,写 ,异常...)。 用户可以进行注册、登录的基本操作 ,将信息存在 MySQL 数据库中。登录的用户通过”map“关联容器进行管 理 ,因为 map 的“按值访问元素”性质 ,契合本项目的需求。使用 QT 中的 API 进行对客户端请求的包装和对服 务器端发来数据的解读。 1. 单人练习模式则从题库中随机抽取 5 道题 ,答对继续 ,打错直接算分退出。 2. 排位赛中规定与你上下 5 颗星可以进行匹配对战 ,如果没有合适人选 ,进入到等待队列。有与之匹配的人方 可进行对战。在对战中可以实时看到对方答题情况。
window---Qt---Client客户端代码
braubstirn.h
#ifndef BRAINSTORM_H
#define BRAINSTORM_H
#include <QDialog>
#include "communicate.h"
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QTimer>
#include <iostream>
#include <QMovie>
#include "./../common.h"
#define QUESTIONTIME 10
#define QUESTION_NUM 5
namespace Ui {
class BrainStorm;
}
class BrainStorm : public QDialog
{
Q_OBJECT
public:
explicit BrainStorm(Communicate *com, QJsonObject &JSON,QWidget *parent = nullptr);
~BrainStorm();
private slots:
void on_singleButton_2_clicked();
void on_single_back_clicked();
void on_pushButton_clicked();
void on_single_start_clicked();
//接受个人训练题目
void receiveSingleQuestion(QJsonObject json);
// 个人训练 计时器
void singleTimerOut();
//开始排位
void Rank(QJsonObject json);
// 排位赛 计时器
void RankTimerOut();
void on_back_pushButton_clicked();
void on_singleSelectButton1_clicked();
void on_singleSelectButton2_clicked();
void on_singleSelectButton3_clicked();
void on_singleSelectButton4_clicked();
void on_pushButton_2_clicked();
void on_rankButton_clicked();
void on_rankSelectButton1_clicked();
void on_rankSelectButton2_clicked();
void on_rankSelectButton3_clicked();
void on_rankSelectButton4_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
private:
//单人训练从服务器获取题目
void singleGetQuestion();
//单人训练设置问题
void singleSetQuestion();
//单人训练回答问题
void singleAnswerQuestion(int select);
//设置对手的得分
void setEnemyScore();
//设置自己的得分
void setSelfScore();
//设置rank题目
void rankSetQuestion();
//排位回答问题
void rankAnswerQuestion(int select);
//设置rank结果
void rankSetResult(QJsonObject &json);
void showEnquire(QJsonObject &json);
private:
Ui::BrainStorm *ui;
Communicate *_com; //通信类指针
QJsonObject _singleQuestionJson; //接收个人训练问题的对象
int _currentSingleQuestion; //个人训练当前问题的标志位(下标)
QTimer _singleTimer; // 个人训练项目定时器
int _singleSec; // 个人训练答题时间
int _singleScore; // 个人训练得分
//------------------- rank ------------------------------------------
QJsonObject _rankQuestion; //rank的问题
QString _enemyName; //对手的名字
QString _enemyRank; //对手的段位
int _enemyScore; //对手的得分
int _rankSec; //rank答题计时
int _selfScore; //个人得分
QString _selfName; //个人用户名
QString _selfRank; //个人段位
int _currentRankQuestion; //rank当前回答问题的下标
int _enemyRankQueston; //对手当前回答问题的下标
QTimer _rankTimer; //rank定时器
};
#endif // BRAINSTORM_H
communicate.h
#ifndef COMMUNICATE_H
#define COMMUNICATE_H
#include <QObject>
#include <QTcpSocket>
#include <QHostAddress>
#include <QJsonObject>
#include <QJsonDocument>
#include "./../common.h"
class Communicate : public QObject
{
Q_OBJECT
public:
explicit Communicate(QObject *parent = nullptr);
void writeData(const QJsonObject &json);
signals:
void rstResult(int);
void Login(QJsonObject json);
void receiveSingleQuestion(QJsonObject);
void Rank(QJsonObject);
void Exit(QJsonObject json);
public slots:
void readData();
private:
QTcpSocket s;
};
#endif // COMMUNICATE_H
login.h
#ifndef LOGIN_H
#define LOGIN_H
#include <QWidget>
#include "communicate.h"
#include "register.h"
#include <QBitmap>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_Login_pushButton_clicked();
void on_Register_pushButton_clicked();
void Login(QJsonObject json);
private:
Ui::Widget *ui;
Communicate *_com; //定义一个通信类指针 负责和服务器通信
};
#endif // LOGIN_H
register.h
#ifndef REGISTER_H
#define REGISTER_H
#include <QDialog>
#include <QJsonObject>
#include "communicate.h"
#include "./../common.h"
#include <QMessageBox>
namespace Ui {
class Register;
}
class Register : public QDialog
{
Q_OBJECT
public:
explicit Register(Communicate *com,QWidget *parent = nullptr);
~Register();
private slots:
void on_RegisterpushButton_clicked();
void on_ReturnpushButton_clicked();
void rstResult(int );
private:
Ui::Register *ui;
Communicate *_com; //定义一个通信类指针 负责和服务器通信
};
#endif // REGISTER_H
braubstirn.cpp
#include "brainstorm.h"
#include "ui_brainstorm.h"
BrainStorm::BrainStorm(Communicate *com, QJsonObject &JSON,QWidget *parent) :
QDialog(parent),
ui(new Ui::BrainStorm)
{
_selfName = JSON["userName"].toString();
_selfRank = JSON["rank"].toString();
ui->setupUi(this);
this->setWindowIcon(QIcon(":/image/question.png"));
this->setWindowTitle("BrainStorm");
ui->rankButton->setStyleSheet("background-color: rgb(0, 170, 255);");
ui->singleQuestion->setStyleSheet("background-color: rgb(0, 170, 255);");
_com = com;
connect(_com,SIGNAL(Exit(QJsonObject)),this,SLOT(Rank(QJsonObject)));
connect(&_rankTimer,SIGNAL(timeout()),this,SLOT(RankTimerOut()));
connect(&_singleTimer,SIGNAL(timeout()),this,SLOT(singleTimerOut()));
connect(_com,SIGNAL(receiveSingleQuestion(QJsonObject)),this,SLOT(receiveSingleQuestion(QJsonObject)));
connect(_com,SIGNAL(Rank(QJsonObject)),this,SLOT(Rank(QJsonObject)));
}
BrainStorm::~BrainStorm()
{
delete ui;
}
// 个人训练 计时器
void BrainStorm::singleTimerOut()
{
--_singleSec;
printf("_singleSec\n");
if(_singleSec == 0)
{
ui->singleResult->setText("回答错误");
QString str = QString("本次得分:%1").arg(_singleScore);
ui->singleScore->setText(str);
_singleTimer.stop();
ui->stackedWidget->setCurrentWidget(ui->single_score);
}
ui->lcdNumber->display(_singleSec);
}
//进入个人训练
void BrainStorm::on_singleButton_2_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->single_menu);
}
//个人训练赛获取题目
void BrainStorm::singleGetQuestion()
{
QJsonObject json;
json["cmd"] = SINGLE_GETQUESTION;
_com->writeData(json);//发送给服务器
}
//设置个人训练题目
void BrainStorm::singleSetQuestion()
{
//题目结束
if(_currentSingleQuestion == (QUESTION_NUM))
{
ui->singleResult->setText("恭喜你,全部回答正确");
QString str = QString("本次得分:%1").arg(_singleScore);
ui->singleScore->setText(str);
_singleTimer.stop();
ui->stackedWidget->setCurrentWidget(ui->single_score);
return;
}
//先转换为数组再转换为字符串
ui->singleQuestion->setText(_singleQuestionJson["question"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton1->setText(_singleQuestionJson["selection1"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton2->setText(_singleQuestionJson["selection2"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton3->setText(_singleQuestionJson["selection3"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton4->setText(_singleQuestionJson["selection4"].toArray().at(_currentSingleQuestion).toString());
}
//接收个人训练题目
void BrainStorm::receiveSingleQuestion(QJsonObject json)
{
_singleQuestionJson = json;
_currentSingleQuestion = 0;
_singleScore = 0;
_singleSec = QUESTIONTIME;
ui->lcdNumber->display(_singleSec);
singleSetQuestion();
//开启定时器
_singleTimer.start(1000);
ui->stackedWidget->setCurrentWidget(ui->single_runing);
}
//返回主界面
void BrainStorm::on_single_back_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
//退出
void BrainStorm::on_pushButton_clicked()
{
close();
}
//开始答题
void BrainStorm::on_single_start_clicked()
{
singleGetQuestion();
}
//个人训练答题
void BrainStorm::singleAnswerQuestion(int select)
{
//回答正确
if(select == _singleQuestionJson["answer"].toArray().at(_currentSingleQuestion).toString().toInt())
{
_singleScore += 20 * _singleSec;
_currentSingleQuestion++;
singleSetQuestion();// 设置下一题
_singleSec = QUESTIONTIME;
_singleTimer.stop();
ui->lcdNumber->display(_singleSec);
_singleTimer.start(1000);
}
else //回答错误
{
ui->singleResult->setText("回答错误");
QString str = QString("本次得分:%1").arg(_singleScore);
ui->singleScore->setText(str);
_singleTimer.stop();
ui->stackedWidget->setCurrentWidget(ui->single_score);
}
}
void BrainStorm::on_back_pushButton_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->single_menu);
}
void BrainStorm::on_singleSelectButton1_clicked()
{
singleAnswerQuestion(1);
}
void BrainStorm::on_singleSelectButton2_clicked()
{
singleAnswerQuestion(2);
}
void BrainStorm::on_singleSelectButton3_clicked()
{
singleAnswerQuestion(3);
}
void BrainStorm::on_singleSelectButton4_clicked()
{
singleAnswerQuestion(4);
}
//-------------------------------------------- rank -----------------------------------------------------
//进入排位赛
void BrainStorm::on_rankButton_clicked()
{
QJsonObject json;
json["cmd"] = RANK;
_com->writeData(json);
//跳转到等待页面
ui->stackedWidget->setCurrentWidget(ui->rank_wait);
}
// 排位赛 计时器
void BrainStorm::RankTimerOut()
{
_rankSec--;
if(_rankSec == 0)
{
if(ui->rankSelectButton1->isEnabled()) //判断是否答题
{
_currentRankQuestion++;
}
rankSetQuestion();
_rankSec = 10;
}
ui->lcdNumber_2->display(_rankSec);
}
//设置对手的得分
void BrainStorm::setEnemyScore()
{
QString str = QString("%1(%2):%3").arg(_enemyName,-5).arg(_enemyRank).arg(_enemyScore);
ui->enemyState->setText(str);
}
//设置自己的得分
void BrainStorm::setSelfScore()
{
QString str = QString("%1(%2):%3").arg(_selfName,-5).arg(_selfRank).arg(_selfScore);
ui->selfState->setText(str);
}
//设置rank题
void BrainStorm::rankSetQuestion()
{
//先转换为数组再转换为字符串
ui->rankQuestion->setText(_rankQuestion["question"].toArray().at(_currentRankQuestion).toString());
ui->rankSelectButton1->setText(_rankQuestion["selection1"].toArray().at(_currentRankQuestion).toString());
ui->rankSelectButton2->setText(_rankQuestion["selection2"].toArray().at(_currentRankQuestion).toString());
ui->rankSelectButton3->setText(_rankQuestion["selection3"].toArray().at(_currentRankQuestion).toString());
ui->rankSelectButton4->setText(_rankQuestion["selection4"].toArray().at(_currentRankQuestion).toString());
//按钮重新被选择
ui->rankSelectButton1->setEnabled(true);
ui->rankSelectButton2->setEnabled(true);
ui->rankSelectButton3->setEnabled(true);
ui->rankSelectButton4->setEnabled(true);
//清空样式
ui->rankSelectButton1->setStyleSheet("");
ui->rankSelectButton2->setStyleSheet("");
ui->rankSelectButton3->setStyleSheet("");
ui->rankSelectButton4->setStyleSheet("");
if(_currentRankQuestion == QUESTION_NUM)
{
//将结果发给服务器
_rankTimer.stop();
//将结果发送给服务器
QJsonObject json;
json["cmd"] = RANKRESULT;
json["score"] = _selfScore;
json["enemyscore"] = _enemyScore;
_com->writeData(json);
}
}
//开始排位
void BrainStorm::Rank(QJsonObject json)
{
int cmd = json["cmd"].toInt();
if(cmd == RANK)
{
_rankQuestion = json["question"].toObject();
_enemyName = json["enemyName"].toString();
_enemyRank = json["enemyRank"].toString();
_enemyScore = json["enemyScore"].toInt();
_rankSec = QUESTIONTIME;
_selfScore = 0;
ui->lcdNumber_2->display(_rankSec);
_currentRankQuestion = 0;
setEnemyScore();
setSelfScore();
rankSetQuestion();
_rankTimer.start(1000);
ui->stackedWidget->setCurrentWidget(ui->rank_running);
}
else if(cmd == ANSWER) //同步进行答题
{
_enemyScore = json["enemyScore"].toInt(); //得分
_enemyRankQueston = json["enemyQuestionId"].toInt(); //题号
setEnemyScore();
if(_enemyRankQueston == _currentRankQuestion)
{
//std::cout<<"EnemyRankQueston = "<<EnemyRankQueston<<" CurrentRankQuestion = "<<CurrentRankQuestion<<std::endl;
_rankSec = QUESTIONTIME;
_rankTimer.stop();
ui->lcdNumber_2->display(_rankSec);
_rankTimer.start(1000);
rankSetQuestion();
}
}
else if(cmd == RANKRESULT)
{
//处理排位结果
rankSetResult(json);
}
else if(cmd == EXIT)
{
_rankTimer.stop();
QJsonObject json;
json["cmd"] = RANKRESULT;
json["enemyscore"] = 0;
json["score"] = 1;
_com->writeData(json);
}
}
void BrainStorm::on_rankSelectButton1_clicked()
{
ui->rankSelectButton1->setStyleSheet("background-color: rgb(170, 170, 255)");
ui->rankSelectButton1->setEnabled(false);
ui->rankSelectButton2->setEnabled(false);
ui->rankSelectButton3->setEnabled(false);
ui->rankSelectButton4->setEnabled(false);
rankAnswerQuestion(1);
}
void BrainStorm::on_rankSelectButton2_clicked()
{
ui->rankSelectButton2->setStyleSheet("background-color: rgb(170, 170, 255)");
ui->rankSelectButton1->setEnabled(false);
ui->rankSelectButton2->setEnabled(false);
ui->rankSelectButton3->setEnabled(false);
ui->rankSelectButton4->setEnabled(false);
rankAnswerQuestion(2);
}
void BrainStorm::on_rankSelectButton3_clicked()
{
ui->rankSelectButton3->setStyleSheet("background-color: rgb(170, 170, 255)");
ui->rankSelectButton1->setEnabled(false);
ui->rankSelectButton2->setEnabled(false);
ui->rankSelectButton3->setEnabled(false);
ui->rankSelectButton4->setEnabled(false);
rankAnswerQuestion(3);
}
void BrainStorm::on_rankSelectButton4_clicked()
{
ui->rankSelectButton4->setStyleSheet("background-color: rgb(170, 170, 255)");
ui->rankSelectButton1->setEnabled(false);
ui->rankSelectButton2->setEnabled(false);
ui->rankSelectButton3->setEnabled(false);
ui->rankSelectButton4->setEnabled(false);
rankAnswerQuestion(4);
}
//排位回答问题
void BrainStorm::rankAnswerQuestion(int select)
{
//计算得分
if(select == _rankQuestion["answer"].toArray().at(_currentRankQuestion).toString().toInt())
{
// _selfScore += 20 * _rankSec;
_selfScore += 20;
}
setSelfScore();
_currentRankQuestion++;
//判断是否进入下一题
if(_enemyRankQueston == _currentRankQuestion)
{
//std::cout<<"EnemyRankQueston = "<<EnemyRankQueston<<" CurrentRankQuestion = "<<CurrentRankQuestion<<std::endl;
_rankSec = QUESTIONTIME;
_rankTimer.stop();
ui->lcdNumber_2->display(_rankSec);
_rankTimer.start(1000);
rankSetQuestion();
}
QJsonObject json;
json["cmd"] = ANSWER;
json["enemyName"] = _enemyName;
json["score"] = _selfScore;
json["questionId"] = _currentRankQuestion;
_com->writeData(json);
}
//设置rank结果
void BrainStorm::rankSetResult(QJsonObject &json)
{
QString newRank = json["newRank"].toString();
if(_selfScore == _enemyScore)
{
ui->resultRank->setText("平局");
}
else if(_selfScore < _enemyScore)
{
ui->resultRank->setText("失败");
}
else if(_selfScore > _enemyScore)
{
ui->resultRank->setText("成功");
}
QString str = QString("%1 ------------> %2").arg(_selfRank).arg(newRank);//段位变化
ui->newRank->setText(str);
_selfRank = newRank;
ui->stackedWidget->setCurrentWidget(ui->rank_result);
}
//排位赛结束按钮返回主界面
void BrainStorm::on_pushButton_2_clicked()
{
ui->rank_result->close();
ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
void BrainStorm::showEnquire(QJsonObject &json)
{
ui->rankChartBrainStorm->clear();
qDebug() << "1111111111111111\n";
QJsonObject json1 = json["order"].toObject();
auto nameJson = json1["name"].toArray();
auto rankJson = json1["rank"].toArray();
int len = json1["name"].toArray().size();
for(int i = 0;i < len;i++)
{
ui->rankChartBrainStorm->append(nameJson.at(i).toString() + ": " + rankJson.at(i).toString());
}
}
//查看段位
void BrainStorm::on_pushButton_3_clicked()
{
QJsonObject json;
json["cmd"] = ENQUIRE;
_com->writeData(json);
ui->stackedWidget->setCurrentWidget(ui->rank_chart);
}
void BrainStorm::on_pushButton_4_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
communicate.cpp
#include "communicate.h"
#include "./../common.h"
Communicate::Communicate(QObject *parent) : QObject(parent)
{
//连接服务器
s.connectToHost(QHostAddress("192.168.90.186"),9999);
connect(&s,SIGNAL(readyRead()),this,SLOT(readData()));
}
/*
QJsonArray 封装 JSON 数组
QJsonDocument 读写 JSON 文档
QJsonObject 封装 JSON 对象
QJsonObject::iterator 用于遍历QJsonObject的 STL 风格的非 const 遍历器
QJsonParseError 报告 JSON 处理过程中出现的错误
QJsonValue 封装 JSON 值
*/
void Communicate::readData()
{
QByteArray data;
while (s.bytesAvailable())
{
data += s.readAll();
}
//数据解析
QJsonDocument dt = QJsonDocument::fromJson(data);
//QJsonDocument::fromJson()可以由QByteArray对象构造一个QJsonDocument对象,用于我们的读写操作。
if(dt.isNull())
{
return;
}
QJsonObject root = dt.object();
//具体的逻辑处理
int cmd = root["cmd"].toInt();
switch (cmd)
{
case REGISTER:
emit rstResult(root["result"].toInt()); //emit 发射信号
break;
case LOGIN:
emit Login(root);
break;
case SINGLE_GETQUESTION:
emit receiveSingleQuestion(root["question"].toObject());
break;
case RANK:
emit Rank(root);
break;
case ANSWER:
emit Rank(root);
break;
case RANKRESULT:
emit Rank(root);
break;
case ENQUIRE:
emit Rank(root);
break;
default:
break;
}
}
void Communicate::writeData(const QJsonObject &json)
{
QJsonDocument d(json);//QJsonDocument:能将对象转变为json字符串
//toJSON() 方法可以将 Date 对象转换为字符串,并格式化为 JSON 数据格式。
QByteArray sendData = d.toJson();
int len = sendData.size();
s.write((char *)&len,sizeof(int)); //发送数据长度
s.write(sendData); //发送数据
}
login.cpp
#include "login.h"
#include "ui_login.h"
#include "register.h"
#include "brainstorm.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//this->setWindowIcon(QIcon(":/image/brain.png"));
this->setWindowTitle("BrainStorm");
QPalette palette = this->palette();
palette.setColor(QPalette::Background,QColor(0x40,0x50,0x60));
this->setPalette(palette);
_com = new Communicate();
connect(_com,SIGNAL(Login(QJsonObject)),this,SLOT(Login(QJsonObject)));
}
Widget::~Widget()
{
delete ui;
}
//登录
void Widget::on_Login_pushButton_clicked()
{
QString userName = ui->UserlineEdit->text();
QString passwd = ui->PasswdlineEdit->text();
//登录操作,将登录信息发送给服务器
QJsonObject json;
json["cmd"] = LOGIN;
json["userName"] = userName;
json["passwd"] = passwd;
//给服务器发送数据
_com->writeData(json);
}
void Widget::Login(QJsonObject json)
{
int result = json["result"].toInt();
switch (result)
{
case OK:
{
this->hide();
BrainStorm *dlg = new BrainStorm(_com,json);
dlg->show();
//退出时自己销毁
dlg->setAttribute(Qt::WA_DeleteOnClose);
// this->show();
break;
}
case ERROR:
QMessageBox::critical(this,"登录","登录失败,未知错误");
break;
case USERLOGIN:
QMessageBox::critical(this,"登录","登录失败,不允许重复登录");
break;
case NAMEORPASSWD:
QMessageBox::critical(this,"登录","用户名或者密码错误");
break;
default:
break;
}
}
//注册
void Widget::on_Register_pushButton_clicked()
{
this->hide(); //当前窗口隐藏
Register reg(_com);
reg.exec();
this->show(); //显示登录窗口
}
main.cpp
#include "login.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
register.cpp
#include "register.h"
#include "ui_register.h"
Register::Register(Communicate *com,QWidget *parent) :
QDialog(parent),
ui(new Ui::Register)
{
ui->setupUi(this);
this->setWindowIcon(QIcon(":/image/register.png"));
this->setWindowTitle("Register");
_com = com;
connect(_com,SIGNAL(rstResult(int)),this,SLOT(rstResult(int )));
}
Register::~Register()
{
delete ui;
}
void Register::on_RegisterpushButton_clicked()
{
QString userName = ui->UserNamelineEdit->text();
QString passwd = ui->PasswdlineEdit->text();
//发送注册信息
QJsonObject json;
json["cmd"] = REGISTER;
json["userName"] = userName;
json["passwd"] = passwd;
//给服务器发送数据
_com->writeData(json);
}
void Register::rstResult(int ret)
{
switch (ret)
{
case OK:
QMessageBox::information(this,"注册","注册成功");
break;
case ERROR:
QMessageBox::critical(this,"注册","未知错误");
break;
case USEREXIST:
QMessageBox::critical(this,"注册","注册失败,用户已经存在");
break;
default:
break;
}
}
void Register::on_ReturnpushButton_clicked()
{
close();
}
linux---Qt---server服务器代码
db.h
#ifndef DB_H
#define DB_H
#include "mysql/mysql.h"
#include <mutex>
#include "spdlog/spdlog.h"
#include "jsoncpp/json/json.h"
class DB
{
public:
DB(const char *host,const char *userName,const char *passwd,const char *dbName);
//执行数据库语句(增、删、改)
bool db_exec(const char *sql);
//数据库查询(查询、保存结果)
// [in] sql 查询语句
// [out] outJson 查询结果保存到JSON变量中
bool db_select(const char *sql,Json::Value &outJson);
private:
std::mutex _mutex; //数据库句柄互斥锁
MYSQL *_mysql ; //数据库句柄
};
#endif // DB_H
myserver.h
#ifndef MYSERVER_H
#define MYSERVER_H
#include "tcpserver.h"
#include <spdlog/spdlog.h>
#include "db.h"
#include <jsoncpp/json/json.h>
#include <jsoncpp/json/reader.h>
#include "./../common.h"
#include <string.h>
#include <map>
#include "user.h"
#define DEBUG
class MyServer : public TcpServer
{
public:
MyServer();
void connectEvent(TcpSocket *);
void readEvent(TcpSocket *);
void writeEvent(TcpSocket *);
void closeEvent(TcpSocket *, short);
private:
// 发送数据给客户端
void writeData(TcpSocket *s, const Json::Value &inJson);
// 用户注册
void Register(TcpSocket *s, const Json::Value &inJson);
// 用户登陆
void Login(TcpSocket *s, const Json::Value &inJson);
// 个人训练获取题目
void singleGetQuestion(TcpSocket *s);
// 初始化段位列表
void initRankMap();
// 排位
void Rank(TcpSocket *s);
// 开始对决
void startRank(TcpSocket *first, TcpSocket *second);
// rank回答一个问题
void RankAnswerQuestion(TcpSocket *s, const Json::Value &inJson);
// rank结果
void RankResult(TcpSocket *s, const Json::Value &inJson);
private:
std::shared_ptr<spdlog::logger> _log; // 记录日志的句柄
DB *_db; // 数据库句柄
// 键是用户名,值是用户指针
std::mutex _userLock;
std::map<std::string, User *> _users; // 在线用户列表
// key rank积分
// 值 对应的段位说明
std::map<int, std::string> _rankMap; // 段位列表
// key rank积分
// 值 等待rank的用户
std::mutex _rankLock;
std::map<int, TcpSocket *> _rankQueue; // 等待排位列表
};
#endif // MYSERVER_H
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include "thread.h"
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include "tcpsocket.h"
class TcpSocket;
//Tcp服务的基类
class TcpServer
{
friend class TcpSocket;
public:
TcpServer(int threadNum = 8);
int listen(int port,const char* ip = NULL);
//服务器开始运行
void start();
protected:
//监听回调函数,有客户端连接的时候会调用
static void listenCb(struct evconnlistener *,evutil_socket_t,struct sockaddr * ,int socklen,void *);
//监听处理函数
void listenEvent(evutil_socket_t fd,struct sockaddr_in *);
//虚函数 具体处理客户端的逻辑
//客户端连接事件
virtual void connectEvent(TcpSocket *){}
//客户端可读
virtual void readEvent(TcpSocket *){}
//可写事件
virtual void writeEvent(TcpSocket *){}
//关闭事件
virtual void closeEvent(TcpSocket * ,short ){}
private:
int m_threadNum; //线程个数
Thread *m_threadPool; //线程池
struct event_base *m_base;
struct evconnlistener *m_listener; //监听客户端的连接
int m_nextThread; //记录下一个线程的下标
};
#endif // TCPSERVER_H
tcpsocket.h
#ifndef TCPSOCKET_H
#define TCPSOCKET_H
#include "tcpserver.h"
#include <string>
class TcpServer;
//通信类 负责与客户端进行通信
class TcpSocket
{
public:
TcpSocket(TcpServer* server,struct bufferevent *bev,char *ip,u_int16_t port);
//可读事件回调函数
static void readEventCb(struct bufferevent *bev,void *ctx);
//可写事件回调函数
static void writeEventCb(struct bufferevent *bev, void *ctx);
//异常事件回调函数
static void closeEventCb(struct bufferevent *bev,short what,void *ctx);
char *getIp(); //获取ip地址
u_int16_t getPort(); //获取端口
//从客户端读数据
int readData(void *data,int size);
//向客户读写数据
int writeData(const void *data ,int size);
void setUserName(std::string name);
std::string getUserName();
private:
static TcpServer *m_tcpServer; //服务器类对象
struct bufferevent *m_bev; //与客户端通信的句柄
char *m_ip; //客户端的ip地址
u_int16_t m_port; //客户端的端口
std::string _userName; //用户名
};
#endif // TCPSOCKET_H
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/event_compat.h>
//线程类
class Thread
{
public:
Thread();
void start(); //线程运行
//获取事件集合
struct event_base *getBase();
protected:
static void* worker(void*); //线程的工作函数
static void pipeRead(evutil_socket_t,short,void*);
void run(); //线程的逻辑处理函数
private:
struct event_base *m_base; //事件集合
pthread_t m_threadId; //线程ID
int m_pipeReadFd; //管道的读端
int m_pipeWriteFd; //管道的写端
struct event m_pipeEvent; //管道事件
};
#endif // THREAD_H
user.h
#ifndef USER_H
#define USER_H
#include <string>
#include "tcpsocket.h"
class User
{
public:
User(std::string n,std::string p,int rank,TcpSocket *s);
TcpSocket *getSocket();
const char *getName();
int getRank();
int setRank(int rank);
private:
std::string _userName; //用户名
std::string _passwd; //用户密码
int _rank; //分数
TcpSocket *_s; //通信套接字类
};
#endif // USER_H
db.cpp
#include "db.h"
DB::DB(const char *host,const char *userName,const char *passwd,const char *dbName)
{
//初始化数据库句柄
_mysql = mysql_init(NULL);
if(_mysql == NULL)
{
spdlog::get("BrainStorm")->error ("mysql_init error!\n");
exit(-1);
}
//连接mysql服务器
MYSQL *con = mysql_real_connect(_mysql,host,userName,passwd,dbName,0,NULL,0);
if(con == NULL)
{
spdlog::get("BrainStorm")->error("连接数据库失败: {}",mysql_error(_mysql));
exit(-1);
}
//设置字符集
int ret = mysql_query(_mysql,"set names utf8");
if(ret != 0)
{
spdlog::get("BrainStorm")->error("设置字符集失败:%s\n",mysql_error(_mysql));
exit(-1);
}
}
bool DB::db_exec(const char *sql)
{
std::unique_lock<std::mutex> loc(_mutex); //数据库句柄上锁
int ret = mysql_query(_mysql,sql);
if(ret != 0)
{
spdlog::get("BrainStorm")->error("mysql_query erroor:%s\n",mysql_error(_mysql));
return false;
}
return true;
}
bool DB::db_select(const char *sql,Json::Value &outJson)
{
std::unique_lock<std::mutex> loc(_mutex); //数据库句柄上锁
int ret = mysql_query(_mysql,sql);
if(ret != 0)
{
spdlog::get("BrainStorm")->error("mysql_query erroor:%s\n",mysql_error(_mysql));
return false;
}
//从mysql服务器下载查询结果
MYSQL_RES *sql_res = mysql_store_result((_mysql));
if( sql_res ==NULL)
{
if(mysql_error(_mysql) == 0)
return true;
else
{
spdlog::get("BrainStorm")->error("mysql_query erroor:%s\n",mysql_error(_mysql));
}
}
MYSQL_ROW row; //从结果中一行一行的取出数据
unsigned int num_fields = mysql_num_fields(sql_res); //获取列数
MYSQL_FIELD *fetch_field = mysql_fetch_field(sql_res); //获取表头
//一行一行获取数据
while(row = mysql_fetch_row(sql_res))
{
for(unsigned int i = 0;i < num_fields;i++)
{
outJson[fetch_field[i].name].append(row[i]);
}
}
mysql_free_result(sql_res);
return true;
}
main.cpp
#include <iostream>
#include "thread.h"
#include <tcpserver.h>
#include "myserver.h"
using namespace std;
int main1()
{
Thread *pt1 = new Thread;
Thread *pt2 = new Thread;
pt1->start();
pt2->start();
while(1)
{
printf("111111\n");
sleep(1);
}
return 0;
}
int main2()
{
TcpServer s;
s.listen(9999);
s.start();
return 0;
}
int main()
{
MyServer s;
s.listen(9999);
s.start();
return 0;
}
myserver.cpp
tcpserver.cpp
#include "tcpserver.h"
TcpServer::TcpServer(int threadNum):m_nextThread(0)
{
if (threadNum <= 0)
{
printf("threadNUm <= 0\n");
exit(-1);
}
//创建线程池
m_threadNum = threadNum;
m_threadPool = new Thread[threadNum];
if (m_threadPool == NULL)
{
printf("create threadPool error\n");
exit(-1);
}
m_base = event_base_new();
if (!m_base)
{
printf("Cloud not initalize libevent!\n");
exit(-1);
}
}
void TcpServer::listenCb(struct evconnlistener *,evutil_socket_t fd,struct sockaddr *clientAdd ,int ,void *data)
{
TcpServer*p = (TcpServer*)data;
p->listenEvent(fd,(struct sockaddr_in *)clientAdd);
}
void TcpServer::listenEvent(evutil_socket_t fd,struct sockaddr_in *clientAddr)
{
char *ip = inet_ntoa(clientAddr->sin_addr); //客户端的ip地址
uint16_t port = ntohs(clientAddr->sin_port); //客户端使用的端口
//从线程池中选择一个线程去处理客户端的请求
//以轮询的方式去选择线程
struct event_base *base = m_threadPool[m_nextThread].getBase();
m_nextThread = (m_nextThread + 1) % m_threadNum;
struct bufferevent *bev = bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);
if (!bev)
{
printf("Error construct bufferevent!");
event_base_loopbreak(base);
return;
}
//创建一个通信对象
TcpSocket *s = new TcpSocket(this,bev,ip,port);
//单独封装一个类负责和客户端的通信
bufferevent_setcb(bev,s->readEventCb,s->writeEventCb,s->closeEventCb,s);
bufferevent_enable(bev,EV_WRITE);
bufferevent_enable(bev,EV_READ);
bufferevent_enable(bev,EV_SIGNAL);
//调用客户端连接事件
connectEvent(s);
}
int TcpServer::listen(int port,const char* ip)
{
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port =htons(port);
if (ip != NULL)
inet_aton(ip,&sin.sin_addr);
//连接,有客户端连接成功自动调用回调函数
m_listener = evconnlistener_new_bind(m_base,listenCb,this,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,-1,(struct sockaddr*)&sin,sizeof(sin));
if (!m_listener)
{
printf("Could not create a listener!\n");
return -1;
}
//开启线程池
for(int i = 0; i < m_threadNum; i++ )
{
m_threadPool[i].start();
printf("线程 %d 启动\n",i+1);
}
return 0;
}
void TcpServer::start()
{
event_base_dispatch(m_base);
evconnlistener_free(m_listener);
event_base_free(m_base);
printf("done\n");
}
tcpsocket.cpp
#include "tcpsocket.h"
TcpServer *TcpSocket::m_tcpServer =NULL;
TcpSocket::TcpSocket(TcpServer *tcpServer,struct bufferevent *bev,char *ip,u_int16_t port)
{
m_tcpServer = tcpServer;
m_bev = bev;
m_ip = ip;
m_port = port;
}
void TcpSocket::readEventCb(struct bufferevent *,void *ctx)
{
TcpSocket *s = (TcpSocket *)ctx;
m_tcpServer->readEvent(s);
}
void TcpSocket::writeEventCb(struct bufferevent *, void *ctx)
{
TcpSocket*s = (TcpSocket *)ctx;
m_tcpServer->writeEvent(s);
}
void TcpSocket::closeEventCb(struct bufferevent *,short what,void *ctx)
{
TcpSocket *s = (TcpSocket *)ctx;
m_tcpServer->closeEvent(s,what);
delete s;
}
char *TcpSocket::getIp()
{
return m_ip;
}
u_int16_t TcpSocket::getPort()
{
return m_port;
}
int TcpSocket::readData(void *data,int size)
{
return bufferevent_read(m_bev,data,size);
}
int TcpSocket::writeData(const void *data ,int size)
{
return bufferevent_write(m_bev,data,size);
}
void TcpSocket::setUserName(std::string name)
{
_userName = name;
}
std::string TcpSocket::getUserName()
{
return _userName;
}
thread.cpp
#include "thread.h"
Thread::Thread()
{
m_base = event_base_new();
if(!m_base)
{
printf("Couldn't create an event_base: exiting\n");
exit(-1);
}
//创建管道
int fd[2];
if (pipe(fd) == -1)
{
perror("pipe");
exit(-1);
}
m_pipeReadFd = fd[0];
m_pipeWriteFd = fd[1];
//让管道事件监听管道的读端
//如果监听到 管道的读端有数据可读,调用读函数pipeRead
event_set(&m_pipeEvent,m_pipeReadFd,EV_READ | EV_PERSIST,pipeRead,this);
//将事件添加到m_base 集合中
event_base_set(m_base,&m_pipeEvent);
//开启事件的监听
event_add(&m_pipeEvent,0);
}
void Thread:: pipeRead(evutil_socket_t,short,void*)
{
}
void Thread::start()
{
//创建一个线程
pthread_create(&m_threadId,NULL,worker,this);
//线程分离
pthread_detach(m_threadId);
}
void* Thread::worker(void* arg)
{
Thread* p = (Thread*)arg;
p->run();
return NULL;
}
void Thread::run()
{
//printf("%d: start\n",m_threadId);
//监听事件集合
//event_base_dispatch 四循环 用来处理事件 类似与QT的exec()
//如果m_base 事件集合为空的话 那么event_base_dispatch会立马返回
//需要初始化m_base添加一个事件 让他不为空
event_base_dispatch(m_base); //一直监听
event_base_free(m_base);
//printf("%d: done\n",m_threadId);
}
struct event_base *Thread::getBase()
{
return m_base;
}
user.cpp
#include "user.h"
User::User(std::string n,std::string p,int rank,TcpSocket *s)
:_userName(n)
,_passwd(p)
,_rank(rank)
,_s(s)
{
}
TcpSocket *User::getSocket()
{
return _s;
}
const char *User::getName()
{
return _userName.c_str();
}
int User::getRank()
{
return _rank;
}
int User::setRank(int rank)
{
if(rank <= 0)
{
_rank = 0;
}
_rank = rank;
}