基于Qt的在线答题系统

实现功能 :

用户的注册及登录 ,个人答题训练 ,排位赛(网络对战) ,排位赛时发送表情、排位系统、积分榜。

项目描述 :

采用 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;
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值