Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)

文章目录

  • 零、项目概述
  • 一、客户端
    • 1.1 Headers
      • 1.1.1 communicate.h
      • 1.1.2 login.h
      • 1.1.3 register.h
      • 1.1.4 training.h
    • 1.2 Sources
      • 1.2.1 communicate.cpp
      • 1.2.2 login.cpp
      • 1.2.3 register.cpp
      • 1.2.4 training.cpp
      • 1.2.5 main.cpp
  • 二、服务器
    • 2.0 Server.pro
    • 2.1 Headers
      • 2.1.1 db.h
      • 2.1.2 myserver.h
      • 2.1.3 tcpserver.h
      • 2.1.4 tcpsocket.h
      • 2.1.5 thread.h
      • 2.1.6 user.h
    • 2.2 Sources
      • 2.2.1 db.cpp
      • 2.2.2 myserver.cpp
      • 2.2.3 tcpserver.cpp
      • 2.2.4 tcpsocket.cpp
      • 2.2.5 thread.cpp
      • 2.2.6 user.cpp
      • 2.2.7 main.cpp
  • 三、协议(common.h)
  • 四、ui界面
    • 4.1 客户端
      • 4.1.1 login.ui
      • 4.1.2 register.ui
      • 4.1.3 training.ui
  • 四、数据库内容
  • 五、效果展示

零、项目概述

开发环境/工具:Ubuntu18.04、Qt、MySQL、libevent、spdlog、
实现功能:用户注册、用户登录、排位匹配对战答题、个人训练答题、段位记录系统、段位匹配系统
项目描述:
1、此项目基于Qt平台,分别在windows和Linux下构建客户端和服务器,实现“头脑风暴”在线对战小游戏
2、采用C/S架构,利用TCP协议通信,通过spdlog在服务器端记录日志信息
3、采用线程池和libevent事件通知库配合实现的高并发服务器
4、采用QT界面编程,提供较为美观的可视化界面,GUI 便于用户使用操作
5、采用 json数据格式进行服务器与客户端之间的数据交换
6、采用MySQL数据库对用户信息和游戏题目进行存储,通过Map容器对在线用户进行记录

一、客户端

1.1 Headers

1.1.1 communicate.h

#ifndef COMMUNICATE_H
#define COMMUNICATE_H

#include 
#include 
#include 
#include 
#include "../common.h"
#include 

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);

public slots:
    void readData();

private:
    QTcpSocket s;
};

#endif // COMMUNICATE_H

1.1.2 login.h

#ifndef WIDGET_H
#define WIDGET_H

#include 
#include "communicate.h"
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_StartButton_clicked();

    void on_RegisterButton_clicked();

    void Login(QJsonObject json);

private:
    Ui::Widget *ui;

    Communicate *_com;  //定义一个通信类指针
};

#endif // WIDGET_H

1.1.3 register.h

#ifndef REGISTER_H
#define REGISTER_H

#include 
#include 
#include "../common.h"
#include "communicate.h"

namespace Ui {
class Register;
}

class Register : public QDialog
{
    Q_OBJECT

public:
    explicit Register(Communicate *com, QWidget *parent = 0);
    ~Register();

private slots:
    void on_RegisterButton_clicked();

    void on_BackButton_clicked();

    void rstResult(int);



private:
    Ui::Register *ui;
    Communicate *_com;
};

#endif // REGISTER_H

1.1.4 training.h

#ifndef TRAINING_H
#define TRAINING_H

#include 
#include "communicate.h"
#include 
#include 
#include 
#define QUESTIONTIME 8
#define QUESTION_NUM 5

namespace Ui {
class training;
}

class training : public QDialog
{
    Q_OBJECT

public:
    explicit training(Communicate *com, QJsonObject JSON, QWidget *parent = 0);
    ~training();

private:
    void singleGetQuestion();

    //个人训练答题
    void singleAnswerQuestion(int select);

    //设置个人训练题目
    void singleSetQuestion();


   // void singleTimerOut();

    void SetSelfLineInfo();

    void SetEnemyLineInfo();

    void RankSetQuestion();

    //排位答题
    void RankAnswerQuestion(int select);

    //处理排位结果
    void RankSetResult(QJsonObject);

private slots:
    void on_singleButton_clicked();

    void on_single_back_clicked();

    void on_single_start_clicked();

    //接受个人训练题目
    void receiveSingleQuestion(QJsonObject json);

    void on_single_SelectButton_one_clicked();

    void on_single_SelectButton_two_clicked();

    void on_single_SelectButton_three_clicked();

    void on_single_SelectButton_four_clicked();

    // 个人训练 计时器
    void  singleTimerOut();

    void on_singnal_score_backButton_clicked();

    void on_rankButton_clicked();

    //开始排位
    void Rank(QJsonObject json);

    void RankTimerOut();

    void on_rankSelectButton1_clicked();

    void on_rankSelectButton2_clicked();

    void on_rankSelectButton3_clicked();

    void on_rankSelectButton4_clicked();

    void on_pushButton_clicked();

    void on_RankWaitBackButton_clicked();

private:
    Ui::training *ui;
    Communicate *_com;  //通信类指针


    QJsonObject _singleQuestionJson; //接收个人训练问题的对象
    int         singleQuestionFlag;  //个人训练当前问题的标志位(下标)
    QTimer      _singleTimer;        // 个人训练项目定时器
    int         _singleSec;          // 个人训练答题时间
    int         _singleScore;        // 个人训练得分

    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 // TRAINING_H

1.2 Sources

1.2.1 communicate.cpp

#include "communicate.h"
#include 
#include 
Communicate::Communicate(QObject *parent) : QObject(parent)
{
    //连接服务器
    s.connectToHost(QHostAddress("192.168.1.170"),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());
        break;
    case LOGIN:
        emit Login(root);
        break;
    case SINGLEGETQUESTION:
        emit receiveSingleQuestion(root["question"].toObject());
        break;
    case RANK:
        emit Rank(root);
        break;
    case ANSWER:
        emit Rank(root);
        break;
    case RANKRESULT:
        emit Rank(root);
        break;
    default:
        break;
    }

}

void Communicate::writeData(const QJsonObject &json)
{
    //QJsonDocument:能将对象转变为json字符串
    QJsonDocument d(json);
    //toJSON() 方法可以将 Date 对象转换为字符串,并格式化为 JSON 数据格式。
    QByteArray sendData = d.toJson();
    int len = sendData.size();

    //发送数据长度
    s.write((char *)&len,sizeof(int));
    //发送数据
    s.write(sendData);
}

1.2.2 login.cpp

#include "login.h"
#include "ui_widget.h"
#include "register.h"
#include 
#include "training.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    _com = new Communicate();
    connect(_com,SIGNAL(Login(QJsonObject)),this,SLOT(Login(QJsonObject)));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_StartButton_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::on_RegisterButton_clicked()
{
    this->hide();
    Register reg(_com);
    reg.exec();
    this->show();

}

void Widget::Login(QJsonObject json)
{
    int result = json["result"].toInt();
    switch (result)
    {
    case OK:
    {
        this->hide();
        training *dlg = new training(_com,json);
        dlg->show();
        //退出时自己销毁
        dlg->setAttribute(Qt::WA_DeleteOnClose);
        break;
    }
    case ERROR:
        QMessageBox::critical(this,"登录","登录失败,未知错误");
        break;
    case USERLOGIN:
        QMessageBox::critical(this,"登录","登录失败,不允许重复登录");
        break;
    case NAMEORPASSWD:
        QMessageBox::critical(this,"登录","用户名或者密码错误");
        break;
    default:
        break;
    }
}

1.2.3 register.cpp

#include "register.h"
#include "ui_register.h"
#include 
#include 

Register::Register(Communicate *com,QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Register)
{
    ui->setupUi(this);
    _com = com;
    connect(_com,SIGNAL(rstResult(int)),this,SLOT(rstResult(int)));
}

Register::~Register()
{
    delete ui;
}

void Register::on_RegisterButton_clicked()
{
    QString userName = ui->UserlineEdit->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_BackButton_clicked()
{
    this->close();
}


1.2.4 training.cpp

#include "training.h"
#include "ui_training.h"
#include 

training::training(Communicate *com,QJsonObject JSON,QWidget *parent) :
    QDialog(parent),
    ui(new Ui::training)
{
    SelfName = JSON["userName"].toString();
    SelfRank = JSON["grade"].toString();
    ui->setupUi(this);
    _com = com;
    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)));
}

training::~training()
{
    delete ui;
}

//个人训练获取题目
void training::singleGetQuestion()
{
    QJsonObject json;
    json["cmd"] = SINGLEGETQUESTION;
    _com->writeData(json);
}

//设置个人训练题目
void training::singleSetQuestion()
{
    //ui->single_question->setText("111111");
    ui->single_question->setText(_singleQuestionJson["question"].toArray().at(singleQuestionFlag).toString());
    ui->single_SelectButton_one->setText(_singleQuestionJson["selection1"].toArray().at(singleQuestionFlag).toString());
    ui->single_SelectButton_two->setText(_singleQuestionJson["selection2"].toArray().at(singleQuestionFlag).toString());
    ui->single_SelectButton_three->setText(_singleQuestionJson["selection3"].toArray().at(singleQuestionFlag).toString());
    ui->single_SelectButton_four->setText(_singleQuestionJson["selection4"].toArray().at(singleQuestionFlag).toString());
}

//个人训练答题
void training::singleAnswerQuestion(int select)
{
    //回答正确
    if(select == _singleQuestionJson["answer"].toArray().at(singleQuestionFlag).toString().toInt())
    {
        _singleScore += 20 * _singleSec;
        if(singleQuestionFlag == (QUESTION_NUM - 1))
        {
            ui->single_Result->setText("恭喜你,全部回答正确");
            QString str = QString("本次得分:%1").arg(_singleScore);
            ui->single_Result_Score->setText(str);
            _singleTimer.stop();
            ui->stackedWidget->setCurrentWidget(ui->single_score);
        }
        singleQuestionFlag++;
        singleSetQuestion();
        _singleSec = QUESTIONTIME;
        _singleTimer.stop();
        ui->lcdNumber->display(_singleSec);
        _singleTimer.start(1000);
    }
else
    {
        ui->single_Result->setText("回答错误");
        QString str = QString("本次得分:%1").arg(_singleScore);
        ui->single_Result_Score->setText(str);
        _singleTimer.stop();
        ui->stackedWidget->setCurrentWidget(ui->single_score);
    }
}

//接受个人训练题目
void training::receiveSingleQuestion(QJsonObject json)
{
    //qDebug()<<"槽函数";
    _singleQuestionJson = json;
    singleQuestionFlag = 0;
    _singleScore = 0;
    _singleSec = QUESTIONTIME;
    ui->lcdNumber->display(_singleSec);
    singleSetQuestion();
    //开启定时器
    _singleTimer.start(1000);
    ui->stackedWidget->setCurrentWidget(ui->single_running);
}

// 个人训练 计时器
void training::singleTimerOut()
{
    --_singleSec;
    printf("_singleSec\n");
    if(_singleSec == 0)
    {
        ui->single_Result->setText("回答错误");
        QString str = QString("本次得分:%1").arg(_singleScore);
        ui->single_Result_Score->setText(str);
        _singleTimer.stop();
        ui->stackedWidget->setCurrentWidget(ui->single_score);
    }
    ui->lcdNumber->display(_singleSec);
}

//个人训练
void training::on_singleButton_clicked()
{
    ui->stackedWidget->setCurrentWidget(ui->single_menu);
}

void training::on_single_back_clicked()
{
    ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}

void training::on_single_start_clicked()
{
    singleGetQuestion();
}

void training::on_single_SelectButton_one_clicked()
{
    singleAnswerQuestion(1);
}

void training::on_single_SelectButton_two_clicked()
{
    singleAnswerQuestion(2);
}

void training::on_single_SelectButton_three_clicked()
{
    singleAnswerQuestion(3);
}

void training::on_single_SelectButton_four_clicked()
{
    singleAnswerQuestion(4);
}



void training::on_singnal_score_backButton_clicked()
{
    ui->single_score->close();
    ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
//--------------------rank-------------------
void training::RankTimerOut()
{
    --RankSec;

    if(RankSec == 0)
    {
        if(ui->rankSelectButton1->isEnabled())
        {
            CurrentRankQuestion++;
        }
        RankSetQuestion();
        RankSec = QUESTIONTIME;
    }
    ui->lcdNumber_2->display(RankSec);
}


void training::SetSelfLineInfo()
{
    QString str = QString("%1(%2) %3").arg(SelfName,-5).arg(SelfRank).arg(SelfScore);
    ui->selfScore->setText(str);
}

void training::SetEnemyLineInfo()
{
    QString str = QString("%1(%2) %3").arg(EnemyName,-5).arg(EnemyRank).arg(EnemyScore);
    ui->enemyScore->setText(str);
}

void training::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 - 1)
    {
        //将结果发送给服务器
        RankTimer.stop();
        QJsonObject json;
        json["cmd"] = RANKRESULT;
        json["score"] = SelfScore;
        json["enemyName"] = EnemyName;
        json["enemyScore"] = EnemyScore;
        _com->writeData(json);
    }
}

//点击进入排位赛
void training::on_rankButton_clicked()
{
    QJsonObject json;
    json["cmd"] = RANK;
    _com->writeData(json);

    //跳转到等待页面
     ui->stackedWidget->setCurrentWidget(ui->rank_wait);
}

//开始排位
void training::Rank(QJsonObject json)
{
    int result_rank = json["cmd"].toInt();
    if(result_rank == RANK)
    {
        RankQuestion = json["question"].toObject();
        EnemyName = json["enemyName"].toString();
        EnemyRank = json["enemyRank"].toString();
        EnemyScore = json["enemyScore"].toInt();

        RankSec = QUESTIONTIME;
        SelfScore = 0;
        CurrentRankQuestion = 0;
        ui->lcdNumber_2->display(RankSec);

        SetSelfLineInfo();
        SetEnemyLineInfo();
        RankSetQuestion();

        RankTimer.start(1000);
        ui->stackedWidget->setCurrentWidget(ui->rank_running);

    }
    else if(result_rank == ANSWER)
    {
       EnemyScore = json["enemyScore"].toInt();
       EnemyRankQueston = json["enemyQuestionId"].toInt();
       SetEnemyLineInfo();
       if(EnemyRankQueston == CurrentRankQuestion)
       {
           //std::cout<<"EnemyRankQueston = "<RankResult->setText("平局");
    }
    else if(SelfScore < EnemyScore)
    {
        ui->RankResult->setText("失败");
    }
    else if(SelfScore > EnemyScore)
    {
        ui->RankResult->setText("成功");
    }
    QString str = QString("%1 --> %2").arg(SelfRank).arg(newRank);
    ui->NewRank->setText(str);
    SelfRank = newRank;

    ui->stackedWidget->setCurrentWidget(ui->rank_Result);
}

//排位答题
void training::RankAnswerQuestion(int select)
{
    //计算得分
    if(select == RankQuestion["answer"].toArray().at(CurrentRankQuestion).toString().toInt())
    {
        SelfScore += 20 * RankSec;
    }
    SetSelfLineInfo();
    CurrentRankQuestion++;
    //判断是否进入下一题
    if(EnemyRankQueston == CurrentRankQuestion)
    {
        //std::cout<<"EnemyRankQueston = "<writeData(json);
}

void training::on_rankSelectButton1_clicked()
{
    ui->rankSelectButton1->setStyleSheet("color : rgb(0, 170, 255)");
    ui->rankSelectButton1->setEnabled(false);
    ui->rankSelectButton2->setEnabled(false);
    ui->rankSelectButton3->setEnabled(false);
    ui->rankSelectButton4->setEnabled(false);
    RankAnswerQuestion(1);
}

void training::on_rankSelectButton2_clicked()
{
    ui->rankSelectButton2->setStyleSheet("color : rgb(0, 170, 255)");
    ui->rankSelectButton1->setEnabled(false);
    ui->rankSelectButton2->setEnabled(false);
    ui->rankSelectButton3->setEnabled(false);
    ui->rankSelectButton4->setEnabled(false);
    RankAnswerQuestion(2);
}

void training::on_rankSelectButton3_clicked()
{
    ui->rankSelectButton3->setStyleSheet("color : rgb(0, 170, 255)");
    ui->rankSelectButton1->setEnabled(false);
    ui->rankSelectButton2->setEnabled(false);
    ui->rankSelectButton3->setEnabled(false);
    ui->rankSelectButton4->setEnabled(false);
    RankAnswerQuestion(3);
}

void training::on_rankSelectButton4_clicked()
{
    ui->rankSelectButton4->setStyleSheet("color : rgb(0, 170, 255)");
    ui->rankSelectButton1->setEnabled(false);
    ui->rankSelectButton2->setEnabled(false);
    ui->rankSelectButton3->setEnabled(false);
    ui->rankSelectButton4->setEnabled(false);
    RankAnswerQuestion(4);
}



void training::on_pushButton_clicked()
{
    ui->rank_Result->close();
    ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}

void training::on_RankWaitBackButton_clicked()
{
    ui->rank_wait->close();
    ui->stackedWidget->setCurrentWidget(ui->mainMenu);
    QJsonObject json;
    json["cmd"] = ABNORMALEXIT;
    _com->writeData(json);
}

1.2.5 main.cpp

#include "login.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}
/*
QJsonArray	            封装 JSON 数组
QJsonDocument	        读写 JSON 文档
QJsonObject	            封装 JSON 对象
QJsonObject::iterator	用于遍历QJsonObject的 STL 风格的非 const 遍历器
QJsonParseError         报告 JSON 处理过程中出现的错误
QJsonValue              封装 JSON 值
*/

二、服务器

2.0 Server.pro

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
CONFIG += C++11

SOURCES += main.cpp \
    thread.cpp \
    tcpserver.cpp \
    tcpsocket.cpp \
    myserver.cpp \
    db.cpp \
    user.cpp

HEADERS += \
    thread.h \
    tcpserver.h \
    tcpsocket.h \
    myserver.h \
    db.h \
    user.h

//给linevent添加库(以及线程)
LIBS += -L/usr/local/lib -levent -lpthread -ljson -lmysqlclient
LIBS += -L/usr/lib/ -lspdlog

2.1 Headers

2.1.1 db.h

#ifndef DB_H
#define DB_H

#include "mysql/mysql.h"
#include 
#include 
#include 
class DB
{
public:
    DB(const char *host, const char *usrName,const char *passwd,const char * dbName);

    //执行数据库语句
    bool db_exec(const char *sql);

    //数据库查询
    //[int] sql        //查询语句
    //[out] outJson    //查询结果保存到json变量中
    bool db_select(const char *sql, Json::Value &outJson);
private:
    std::mutex _mutex;
    MYSQL *_mysql;   //数据库句柄
};

#endif // DB_H

2.1.2 myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include "tcpserver.h"
#include 
#include "db.h"
#include "json/json.h"
#include "json/reader.h"
#include "../common.h"
#include 
#include 
#include "user.h"
#include "spdlog/sinks/stdout_color_sinks.h"

#define DEBUG
#define QUESTION_NUM 5

class Myserver:public TcpServer
{
public:
    Myserver();
    //客户端连接事件
    void connectEvent(TcpSocket *);

    //客户端可读
    void readEvent(TcpSocket *s);

    //可写事件
    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 _log;   //记录日志的句柄
    DB *_db;   //数据库句柄

    //健是用户名,值是用户指针
    std::mutex _userLock;                   //锁,锁_users的
    std::map _users;    //在线用户列表

    //key  rank积分
    //值   对应的段位说明
    std::map _rankMap;     //段位列表

    //key  rank积分
    //值   等待rank的用户
    std::mutex _rankLock;
    std::map _rankQueue;   //等待排位列表
};

#endif // MYSERVER_H

2.1.3 tcpserver.h

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include "thread.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include "tcpsocket.h"
#include "string.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

2.1.4 tcpsocket.h

#ifndef TCPSOCKET_H
#define TCPSOCKET_H

#include "tcpserver.h"
#include 

class TcpServer;
//通信类,负责与客户端进行通信
class TcpSocket
{
public:
    TcpSocket(TcpServer *tcpServer, struct bufferevent *bev, char *ip, u_int16_t port);

    //在类中使用回调函数必须用static进行修饰
    //可读事件回调函数
    static void readEventCb(struct bufferevent *bev, void *ctx);//ctx就是那个s
    //创建一个通信对象
    //TcpSocket *s = new TcpSocket(this,bev,ip,port);

    //可写事件回调函数
    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

2.1.5 thread.h

#ifndef THREAD_H
#define THREAD_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//线程类
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_pipWirteFd;              //管道写端
    struct event m_pipeEvent;      //管道事件
};

#endif // THREAD_H

2.1.6 user.h

#ifndef USER_H
#define USER_H
#include 
#include 


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 _usrName;   // 用户名
    std::string _passwd;    // 用户密码
    int _rank;              // 分数

    TcpSocket *_s;          // 通信套接字
};

#endif // USER_H

2.2 Sources

2.2.1 db.cpp

#include "db.h"

DB::DB(const char *host, const char *usrName, const char *passwd, const char *dbName)
{
    //初始化数据库句柄
    _mysql = mysql_init(NULL);
    if(_mysql == NULL)
    {
        spdlog::get("log")->error("mysql init error\n");
        exit(-1);
    }
    //连接数据库服务器
    MYSQL *con = mysql_real_connect(_mysql,host,usrName,passwd,dbName,0,NULL,0);
    //第一个参数是数据库句柄
    //第二个参数是主机host
    //第三个参数是用户名
    //第四个参数是密码
    //第五个参数是所要打开的数据库,后面的参数就不需要了
    if(con ==NULL)
    {
        spdlog::get("log")->error("数据库连接失败:{}",mysql_error(_mysql));
        exit(-1);
    }
    _mysql = con;
    //设置字符集,使其支持中文
    int ret = mysql_query(_mysql,"set names utf8");
    if(ret != 0)
    {
        spdlog::get("log")->error("设置字符集失败:{}",mysql_error(_mysql));
        exit(-1);
    }

}
bool DB::db_exec(const char *sql)
{
    std::unique_lock loc(_mutex);   //数据库句柄上锁
    int ret = mysql_query(_mysql,sql);
    //mysql_query() 函数执行一条 MySQL 查询。
    if(ret != 0)
    {
        spdlog::get("log")->error("设置字符集失败:{}",mysql_error(_mysql));
        return false;
    }
    return true;
}
bool DB::db_select(const char *sql, Json::Value &outJson)
{
    std::unique_lock loc(_mutex);   //数据库句柄上锁
    int ret = mysql_query(_mysql,sql);
    //mysql_query() 函数执行一条 MySQL 查询。
    if(ret != 0)
    {
        spdlog::get("log")->error ("mysql_query error:%s",mysql_error(_mysql));
        return false;
    }
    //从mysql服务器下载查询结果
    MYSQL_RES *sql_res = mysql_store_result(_mysql);
    if(NULL == sql_res)
    {
        if(mysql_errno(_mysql) == 0)
        {
            return true;
        }
        else
        {
            spdlog::get("log")->error ("mysql_store_result error:%s",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;
}


2.2.2 myserver.cpp

#include "myserver.h"

Myserver::Myserver()
{
#ifdef DEBUG
    //_log = spdlog::stdout_color_mt("log");
    auto console = spdlog::stdout_color_mt("log");
    //函数创建一个名字为_log的console logger,
    //把这个logger注册到spdlog的全局注册表中,并且返回指向这个logger的指针(shared_ptr)。
#else
    _log = spdlog::rotating_logger_mt("log","log",1024*1024 * 5,3);
    /*
    rotating_logger_mt
    第一个参数是句柄名称
    第二个参数是文件名称
    第三个是日志大小
    第四个参数是多少个文件进行循环
    */
    _log->flush_on(spdlog::level::info);  //显示的权限等级

#endif
    _db = new DB(NULL,"qt","qt","qt_online_answer");
    initRankMap();

}
//客户端连接事件
void Myserver::connectEvent(TcpSocket *s)
{
    spdlog::get("log")->info("有一个新连接:[{}:{}]",s->getIp(),s->getPort());
}

//客户端可读
void Myserver::readEvent(TcpSocket *s)
{
    char buf[1024] = {0};
    memset(buf,0,sizeof(buf));
    while(1)
    {
        int len = 0;
        s->readData(&len,sizeof(len));
        if(len <= 0)
        {
            break;
        }
        s->readData(buf,len);

        //数据解析
        Json::Value root;
        Json::Reader reader;  //json解析器
        //将buf转化成root形式
        if(!reader.parse(buf,root))
        {
            spdlog::get("log")->error("json 数据解析失败");
            return;
        }

        int cmd = root["cmd"].asInt();
        switch (cmd)
        {
        case REGISTER:
            Register(s,root);
            break;
        case LOGIN:
            Login(s,root);
            break;
        case SINGLEGETQUESTION:
            singleGetQuestion(s);
            break;
        case RANK:
            Rank(s);
            break;
        case ANSWER:
            RankAnswerQuestion(s,root);
            break;
        case RANKRESULT:
            RankResult(s,root);
            break;
        default:
            break;
        }

    }
}

//可写事件
void Myserver::writeEvent(TcpSocket *)
{

}

//关闭事件
void Myserver::closeEvent(TcpSocket *s,short)
{
    {
        std::unique_lock lock(_rankLock);
        int rank = _users[s->getUserName()]->getRank();
        auto it = _rankQueue.find(rank);
        if(it != _rankQueue.end())
        {
                _rankQueue.erase(it);
        }
    }
    std::unique_lock lock(_userLock);
    std::map::iterator it = _users.begin();
    while (it != _users.end())
    {
        if(it->second->getSocket() == s)
        {
            _users.erase(it);
            spdlog::get("log")->info("用户{}[{}:{}] log out",it->second->getName(),s->getIp(),s->getPort());
            //释放User
            delete it->second;
            return;
        }
        it++;
    }
    spdlog::get("log")->info("[{}:{}] log out",s->getIp(),s->getPort());
}

void Myserver::writeData(TcpSocket *s,const Json::Value &inJson)
{
    std::string data = inJson.toStyledString();
    s->writeData(data.c_str(),data.length());
}

void Myserver::Register(TcpSocket *s,const Json::Value &inJson)
{
    std::string userName = inJson["userName"].asString();
    std::string passwd = inJson["passwd"].asString();

    //检测用户是否存在
    char sql[1024] = {0};
    sprintf(sql,"select * from user where name = '%s' and passwd = '%s'",userName.c_str(),passwd.c_str());

    int result = OK;
    Json::Value outJson;
    bool ret = _db->db_select(sql, outJson);
    if(!ret)
    {
        result = ERROR;
        spdlog::get("log")->error("Register select user error");
    }

    if(outJson.isMember("name"))   //用户存在,表明已经注册过了
    {
        result = USEREXIST;
    }
    else
    {
        memset(sql,0,sizeof(sql));
        sprintf(sql,"insert into user(name,passwd,rank) values('%s','%s',0)",userName.c_str(),passwd.c_str());
        bool ret = _db->db_select(sql, outJson);
        if(!ret)
        {
            result = ERROR;
            spdlog::get("log")->error("Register insert user error");
        }
        else
        {
            spdlog::get("log")->info("Register user = {} success",userName);
        }
    }
    Json::Value json;
    json["cmd"]    = REGISTER;
    json["result"] = result;

    writeData(s,json);
}

void Myserver::Login(TcpSocket *s,const Json::Value &inJson)
{
    std::string userName = inJson["userName"].asString();
    std::string passwd = inJson["passwd"].asString();
    //int rank = inJson["rank"].asInt();
    int i = 0;
    int rank;
    //检测用户是否已经注册
    char sql[1024] = {0};
    sprintf(sql,"select * from user where name = '%s' and passwd = '%s'",userName.c_str(),passwd.c_str());

    int result = OK;
    Json::Value outJson;
    bool ret = _db->db_select(sql, outJson);
    if(!ret)
    {
        result = ERROR;
        spdlog::get("log")->error("Login select user error");
    }

    if(outJson.isMember("name"))   //用户存在,表明已经注册过了
    {
        std::unique_lock lock(_userLock);
        if(_users.find(userName) != _users.end())  //用户已经登陆
        {
            result = USERLOGIN;
            spdlog::get("log")->info("用户{}[{}:{}] 已经登陆",userName,s->getIp(),s->getPort());
        }
        else
        {
            rank = atoi(outJson["rank"][i].asString().c_str());
            User* user = new User(userName,passwd,rank,s);
            _users.insert(make_pair(userName,user));
            s->setUserName(userName);
            spdlog::get("log")->info("用户{}[{}:{}] login",userName,s->getIp(),s->getPort());
        }
    }
    else
    {
        result = NAMEORPASSWD;
    }
    Json::Value json;
    json["cmd"]      = LOGIN;
    json["result"]   = result;
    json["userName"] = userName;
    json["rank"]     = rank;
    json["grade"]    = _rankMap[rank];
    //spdlog::get("log")->info("用户rank:{}\n",rank);

    writeData(s,json);
}

void Myserver::singleGetQuestion(TcpSocket *s)
{
    char sql[1024] = {0};
    memset(sql,0,sizeof(sql));
    sprintf(sql,"select * from question order by rand() limit %d",QUESTION_NUM);

    int result = OK;
    Json::Value outJson;
    bool ret = _db->db_select(sql, outJson);
    if(!ret)
    {
        result = ERROR;
        spdlog::get("log")->error("Login select user error");
    }
    Json::Value json;
    json["cmd"]      = SINGLEGETQUESTION;
    json["result"]   = result;
    json["question"] = outJson;
    spdlog::get("log")->info("用户{}[{}:{}] 获取题目:{}\n",s->getUserName(),s->getIp(),s->getPort(),json.toStyledString());

    writeData(s,json);
}
//-----------------rank-------------------

//初始化段位列表
void Myserver::initRankMap()
{
    char buf[100];
    int rank = 0;
    int star = 0;
    for(int i = 0;i < 100; i++)
    {
        memset(buf,0,sizeof(buf));
        if(i < 9)
        {
            rank = i / 3;
            star = i % 3;
            sprintf(buf,"青铜%d %d颗星", 3-rank, star+1);
        }
        else if(i < 18)
        {
            rank = (i - 9) / 3;
            star = (i - 9) % 3;
            sprintf(buf,"白银%d %d颗星", 3-rank, star+1);
        }
        else if(i < 34)
        {
            rank = (i - 18) / 4;
            star = (i - 18) % 4;
            sprintf(buf,"黄金%d %d颗星", 4-rank, star+1);
        }
        else if(i < 50)
        {
            rank = (i - 34) / 4;
            star = (i - 34) % 4;
            sprintf(buf,"铂金%d %d颗星", 4-rank, star+1);
        }
        else if(i < 75)
        {
            rank = (i - 50) / 5;
            star = (i - 50) % 5;
            sprintf(buf,"钻石%d %d颗星", 5-rank, star+1);
        }
        else if(i < 100)
        {
            rank = (i - 75) / 5;
            star = (i - 75) % 5;
            sprintf(buf,"星曜%d %d颗星", 5-rank, star+1);
        }
        _rankMap.insert(std::make_pair(i,buf));

    }
//    for(int i = 0;i < 100; i++)
//    {
//        std::cout<< i <<":"<<_rankMap[i]<info("cmd:{}enemyScore:{}enemyQuestionId:{}",ANSWER,inJson["score"].asInt(),inJson["questionId"].asInt());

    writeData(user->getSocket(),json);
}

//开始排位
void Myserver::Rank(TcpSocket *s)
{
    TcpSocket *other = NULL;   //对手
    int rank = _users[s->getUserName()]->getRank();    //当前用户 rank 积分

    std::unique_lock lock(_rankLock);
    //查找同一段位的对手
    std::map::iterator it = _rankQueue.find(rank);
    if(it != _rankQueue.end())
    {
        other = it->second;
        _rankQueue.erase(it);
    }
    else
    {
        //查找其他段位的选手  积分差值的绝对值小于5的都可以进行对决
        for(int i = 1;i <= 5; i++)
        {
            it = _rankQueue.find(rank + i);
            if(it != _rankQueue.end())
            {
                //spdlog::get("log")->info("it->first:{}\n",it->first);
                other = it->second;
                _rankQueue.erase(it);
                break;
            }
            it = _rankQueue.find(rank - i);
            if(it != _rankQueue.end())
            {
                //spdlog::get("log")->info("it->first:{}\n",it->first);
                other = it->second;
                _rankQueue.erase(it);
                break;
            }
        }
    }
    spdlog::get("log")->info("当前分数:{}",rank);
    if(other == NULL) //没有匹配到用户
    {
        _rankQueue.insert(std::make_pair(rank,s));
        spdlog::get("log")->info("当前等候 rank 人数:{}",_rankQueue.size());
    }
    else //找到
    {
        startRank(s,other);
        //开始对决
    }


}

//开始对决
void Myserver::startRank(TcpSocket *first,TcpSocket *second)
{
    char sql[1024] = {0};
    memset(sql,0,sizeof(sql));
    sprintf(sql,"select * from question order by rand() limit %d",QUESTION_NUM);

    int result = OK;
    Json::Value outJson;
    bool ret = _db->db_select(sql, outJson);
    if(!ret)
    {
        result = ERROR;
        spdlog::get("log")->error("startRank select question error");
    }
    Json::Value json;
    json["cmd"]      = RANK;
    json["result"]   = result;
    json["question"] = outJson;

    //first user
    json["enemyName"] = second->getUserName();
    json["enemyRank"] = _rankMap[_users[second->getUserName()]->getRank()];
    json["enemyScore"] = 0;
    //spdlog::get("log")->info("enemyName = {},enemyRank = {},enemyScore = {}",second->getUserName(),_rankMap[_users[second->getUserName()]->getRank()],_users[second->getUserName()]->getRank());
    writeData(first,json);

    //second user
    json["enemyName"] = first->getUserName();
    json["enemyRank"] = _rankMap[_users[first->getUserName()]->getRank()];
    writeData(second,json);

    spdlog::get("log")->info("获取题目:{}\n",json.toStyledString());
}

//rank结果
void Myserver::RankResult(TcpSocket *s,const Json::Value &inJson)
{
    std::unique_lock lock(_userLock);
    User *user = _users[s->getUserName()];

    int score = inJson["score"].asInt();
    int enemyScore = inJson["enemyScore"].asInt();

    if(score < enemyScore)
    {
        user->setRank(user->getRank() - 1);
    }
    else if(score > enemyScore)
    {
        user->setRank(user->getRank() + 1);
    }

    Json::Value json;
    json["cmd"] = RANKRESULT;
    json["newRank"] = _rankMap[user->getRank()];
    writeData(s,json);

}

2.2.3 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[m_threadNum];
    if(m_threadPool == NULL)
    {
        printf("create threadPool error!\n");
        exit(-1);
    }
    m_base = event_base_new();
    if(!m_base)
    {
        printf("Clouldn't create an event_base:exiting\n");
        exit(-1);
    }
}

void TcpServer::listenCb(struct evconnlistener *,evutil_socket_t fd,struct sockaddr *clientAdd,int ,void *date)
{
    //第一个参数是listen的监听事件
    //第二个参数与客户端通信的文件描述符
    //第三个参数客户端的IP地址
    //第五个参数是回调函数参数
    TcpServer *p = (TcpServer *)date;
    p->listenEvent(fd,(struct sockaddr_in *)clientAdd);

}
void TcpServer::listenEvent(evutil_socket_t fd,struct sockaddr_in *clientAdd)
{
    char *ip = inet_ntoa(clientAdd->sin_addr);    //客户端的IP地址
    uint16_t port = ntohs(clientAdd->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);
    //功能:创建一个用于socket的bufferevent,用来缓存套接字读或写的数据,然后调用对应的回调函数
    //base:表示event_base
    //options:是bufferevent选项的位掩(BEV_OPT_CLOSE_ON_FREE等)。
    //fd:参数是一个可选的socket文件描述符。如果希望以后再设置socket文件描述符,可以将fd置为-1。
    //bev客户端句柄
    if(!bev)
    {
        printf("Error constructing bufferevent!");
        event_base_loopbreak(base);
        return;
    }
    //创建一个通信对象
    TcpSocket *s = new TcpSocket(this,bev,ip,port);
    //单独封装一个类负责和客户端的通信
    bufferevent_setcb(bev,s->readEventCb,s->writeEventCb,s->closeEventCb,s);
    //该函数的作用主要是赋值,把该函数后面的参数,赋值给第一个参数struct bufferevent *bufev定义的变量
    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_family主要用来定义是哪种地址族

    sin_port主要用来保存端口号

    sin_addr主要用来保存IP地址信息
    */
    sin.sin_family = AF_INET;  //指定IP地址地址版本人为IPV4
    sin.sin_port = htons(port);  //存放端口信息
    if(ip != NULL)
    {
        //功能:将一个字符串表示的点分十进制IP地址IP转换为网络字节序存储在addr中,并且返回该网络字节序表示的无符号整数。
        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));
    /*
    base: event_base对象。参考event_base。
    cb: 回调函数, 当有新的TCP连接发生时,会唤醒回调函数。
    ptr: 传递给回调函数的参数。

    flags: 一些标志, 后面会进一步介绍。
    //LEV_OPT_CLOSE_ON_FREE:释放listener时,先关闭潜在的套接字
    //LEV_OPT_REUSEABLE:一些系统平台在默认情况下即使已经关闭了socket,也要等待一段时间才能使用对应的port,
    //而这个flag则设置成只要已关闭socket就是重新使用对应的port
    backlog: 监听队列允许容纳的最大连接数。

    sa: evconnlistener_new_bind帮助我们绑定监听地址。sa就是传入的监听地址。
    socklen: sa的长度。
    */
    if(!m_listener)
    {
        printf("Couldn't create alistener\n");
        exit(1);
        return -1;
    }

    //开启线程池
    for(int i = 0;i 

2.2.4 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;
}





2.2.5 thread.cpp

#include "thread.h"

Thread::Thread()
{
    m_base = event_base_new();
    if(!m_base)
    {
        printf("Clouldn'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_pipWirteFd = fd[1];

    //让管道事件监听管道读端
    //如果监听到 管道的读端有数据可读
    event_set(&m_pipeEvent,m_pipeReadFd,EV_READ | EV_PERSIST,pipeRead,this);
            //这就是主线程,不仅可以监听,而且可以防止base里面事件不为空;

    //将事件添加到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);
    //第一参数线程ID
    //属性一般为NULL
    //线程函数(线程要去执行的函数)
    //传给线程函数的参数

    //线程分离
    pthread_detach(m_threadId);
}

void* Thread::worker(void *arg)
{
    Thread *p = (Thread *)arg;
    p->run();
   // sleep(10);
    return NULL;
}

 void Thread::run()
 {
//     while(1)
//     {
//         printf("thread = %d\n",m_threadId);
//         sleep(2);
//     }
     //printf("%d:start",m_threadId);
     //监听base事件集合  event_base_dispatch 死循环 类似Qt 的exec()
     //如果 m_base 事件集合内部是空的花,则event_base_dispatch会立马返回
     //初始化的时候,需要给m_base
     event_base_dispatch(m_base);
     event_base_free(m_base);
     printf("%d:close",m_threadId);

     return ;
 }

 struct event_base *Thread::getBase()
 {
     return m_base;
 }

2.2.6 user.cpp

#include "user.h"

User::User(std::string n,std::string p,int rank,TcpSocket *s):
    _usrName(n),
_passwd(p),
_rank(rank),
_s(s)
{

}

TcpSocket *User::getSocket()
{
    return _s;
}

const char *User::getName()
{
    return _usrName.c_str();
}

int User::getRank()
{
    return _rank;
}

int User::setRank(int rank)
{
    if(rank <= 0)
    {
        _rank = 0;
    }
    _rank = rank;
}

2.2.7 main.cpp

#include 
#include "thread.h"
#include "tcpserver.h"
#include "myserver.h"

using namespace std;

int main1()
{
//    cout << "Hello World!" << endl;
    Thread *pt1 = new Thread;
    Thread *pt2 = new Thread;

    pt1->start();
    pt2->start();
    //printf("11111111\n");
    while (1) {
       printf("11111111\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;
}





三、协议(common.h)

#ifndef COMMON_H
#define COMMON_H

//5XXX  用户操作指令
#define OK                 5000
#define REGISTER           5001  //注册
#define LOGIN              5002  //登录
#define SINGLEGETQUESTION  5003  //个人训练获取问题
#define RANK               5004  //进入排位模式
#define ANSWER             5005  //排位回答一个问题
#define RANKRESULT         5006  //排位结果

//8XXX  错误

#define ERROR          8001  //未知错误
#define USEREXIST      8002  //用户已经存在
#define NAMEORPASSWD   8003  //用户名或者密码错误
#define USERLOGIN      8004  //用户已经登陆
#define ABNORMALEXIT   8005  //用户异常退出

#endif

四、ui界面

4.1 客户端

4.1.1 login.ui

Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第1张图片
Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第2张图片

4.1.2 register.ui

Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第3张图片
Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第4张图片

4.1.3 training.ui

Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第5张图片
Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第6张图片
Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第7张图片

四、数据库内容

Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)_第8张图片

五、效果展示

Qt入门(九)——“头脑风暴”(在线答题

你可能感兴趣的:(Qt入门,qt,开发语言,ui)