目录
一 项目描述
二 段位介绍
三 效果展示
四 服务器代码
1.common.h
2.db.h(数据库类)
3.myserver(处理客户端事件)
4.Tcpserver(监听)
5.Tcpsocket(与客户端通信)
6.Thread.h(线程池)
7.User.h(用户类)
main.cpp
db.cpp
myserver.cpp
Tcpserver.cpp
Tcpsocket.cpp
Thread.cpp
User.cpp
五 客户端代码展示
brainstorm.h(具体功能)
communicate.h(通讯)
login.h
register.h
brainstorm.cpp
communicate.cpp
login.cpp
main.cpp
register.cpp
七 界面展示
编辑
1、该项目采用C/S结构基于TCP/IP协议,运用Libevent网络库和线程池进行搭建服务器框架。在传输数据中采用Json进行轻量级的数据格式转换。使用spdlog日志库来记录操作信息、出错信息和提醒消息。
2、使用MySQL数据库来存储注册账号信息(段位,分数,名字,密码等),创建一个数据库专门用于存储题目
3、单人训练模式:从mysql题库中随机抽取5道题目进行答题,答对继续作答,答错退出答题界面,最后计算的分
4、答题时间设置为10秒,超过答题时间记作答题错误,最终得分以每题计20分进行统计
5、排位系统:设置用户段位星级登记,只有在上下偏差5颗星的范围内才会匹配成功,如果没有匹配成功,进入到等待队列。有匹配成功的,则将玩家移除等待队列进行答题比赛,答题结束后比较玩家得分情况进行段位改动,并将新的段位记录在数据库中
共计段位
青铜3 1星 到 星耀1 5颗星
青铜3个段位1--3 每个段位3颗星 9
白银3个段位1--3 每个段位3颖星 9
黄金4个段位1-—4 每个段位4颗星 16
铂金4个段位1--4 每个段位4颖星 16
钻石5个段位1--5 每个段位5颗星 25
星耀5个段位1--5 每个段位5颗星 25
Brainstorm
#ifndef COMMOM_H
#define COMMON_H
//用户操作指令
#define OK 5000
#define REGISTER 5001
#define LOGIN 5002
#define SINGLE_GETQUESTION 5003 //个人训练获取题目
#define RANK 5004 //进行排位赛
#define ANSWER 5005 //发送自己得分返回对手的 排位回答一个问题
#define RANKRESULT 5006 //答题结果发送给服务器 rank结束
// 错误
#define ERROR 8001 //未知错误
#define USEREXIST 8002 //用户已经存在
#define NAMEORPASSWD 8003//用户名或者密码错误
#define USERLOGIN 8004 //用户已经存在
#define QUESTION_NUM 5 //单次答题5个
#endif // COMMON_H
#ifndef DB_H
#define DB_H
#include "mysql/mysql.h"
#include
#include "spdlog/spdlog.h"
#include
class DB
{
public: //主机 用户名 密码 数据库名称
DB(const char *host, const char *userName,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
#ifndef MYSERVER_H
#define MYSERVER_H
#include "tcpserver.h"
#include
#include "db.h"
#include
#include
#include "../common.h"
#include
#include
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include "thread.h"
#include
#include
#include
#include
#include
#include
#include "tcpsocket.h"
class TcpSocket;
class Tcpserver
{
friend class TcpSocket;
public:
Tcpserver(int threadNum = 8);//默认线程为8个
int listen(int port,const char * ip = NULL); //监听ip
//让服务器开始运行
void start();
protected:
//用户监听回调函数,有客户端连接时调用该函数
static void listenCb(struct evconnlistener *,evutil_socket_t fd,struct sockaddr*clientAdd,int ,void *data);
//监听处理函数
void listenEvent(evutil_socket_t fd,struct sockaddr_in*clientAddr);
//*************************虚函数取具体处理客户端的逻辑*************************
//客户端链接事件
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
#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 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
#ifndef THREAD_H
#define THREAD_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
class Thread
{
public:
Thread();
void start();//线程运行
//获取事件集合base
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; //线程 I D
int m_pipeReadFd; //管道的读段
int m_pipeWriteFd; //管道的写段
struct event m_pipeEvent; //管道事件
};
#endif // THREAD_H
#ifndef USER_H
#define USER_H
#include "tcpsocket.h"
#include
class User
{
public:
User(std::string n,std::string p,int rank,TcpSocket *s);
TcpSocket *getSocket();
const char *getName();
int getRank();
void setRank(int rank);
private:
std::string _userName; //用户名
std::string _passwd; //用户密码
int _rank; //rank分数
TcpSocket *_s; //通信套接字类
};
#endif // USER_H
#include
#include "thread.h"
#include "tcpserver.h"
#include "myserver.h"
#include
using namespace std;
int main()
{
myserver s;
s.listen(8787);
s.start();
return 0;
}
#include "db.h"
DB::DB(const char *host, const char *userName,const char *passwd,const char *dbName)
{
printf("正在构造DB\n");
//初始化数据库句柄
_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("链接数据库失败:{}\n",mysql_error(_mysql));
exit(-1);
}
if(con == _mysql)
{
printf("链接数据库成功\n");
}
_mysql = con;
//设置字符集
int ret = mysql_query(_mysql,"set names utf8");
if(ret != 0)
{
spdlog::get("BrainStorm")->error("设置字符集失败:%s",mysql_error(_mysql));
exit(-1);
}
}
bool DB::db_exec(const char *sql)
{
std::unique_lock loc(_mutex);//数据库句柄上锁
int ret = mysql_query(_mysql,sql);
if(ret != 0)
{
spdlog::get("BrainStorm")->error("mysql_query error :%s",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);
if(ret != 0)
{
spdlog::get("BrainStorm")->error("mysql_query error :%s",mysql_error(_mysql));
return false;
}
//从myaql服务器下载查询结果
MYSQL_RES *sql_res = mysql_store_result(_mysql);
if(sql_res == NULL)
{
if(mysql_errno(_mysql) == 0)
{
return true;
}
else
{
spdlog::get("BrainStorm")->error("mysql_query 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;
}
#include "myserver.h"
using namespace spdlog;
myserver::myserver()
{
initRankMap();
#ifdef DEBUG
_log = spdlog::stdout_color_mt("BrainStorm");
#else
_log = spdlog::rotating_logger_mt("BrainStorm","BrainStorm",1024*1024*5,3);
_log->flush_on(spdlog::level::info);
#endif
_db = new DB(NULL,"root","123456","BrainStorm");
}
void myserver::connectEvent(TcpSocket *s)
{
_log->info("有一个新连接[{}:{}]",s->getIp(),s->getPort());
}
void myserver::readEvent(TcpSocket *s)
{
char buf[1024] = {0};
while (1)
{
int len = 0;
s->readData(&len,sizeof(len));
if( len<= 0)
{
break;
}
s->readData(buf,len);
//发过来的是json数据 进行数据解析
Json::Value root;
Json::Reader reader;//json解析器
if(!reader.parse(buf,root))
{
_log->error("数据解析失败");
return;
}
int cmd = root["cmd"].asInt();
switch (cmd)
{
case REGISTER: //注册
Register(s,root);
break;
case LOGIN: //登录
Login(s,root);
break;
case SINGLE_GETQUESTION: //获取个人训练赛题目
singleGetQuestion(s);
break;
case RANK: //进行排位
Rank(s);
break;
case ANSWER: //答完一题进行消息的转发
rankAnswerOneQuestion(s,root);
break;
case RANKRESULT: //答题结果
rankResult(s,root);
break;
default:
break;
}
}
}
void myserver::writeEvent(TcpSocket *)
{
}
void myserver::closeEvent(TcpSocket *s,short)
{
auto it1 = _users.find(s->getUserName());
if(it1 == _users.end())
{
_log->info("[{}:{}] loginout ",s->getIp(),s->getPort());
}
else
{
char sql[100] = {0};
printf("测试1\n");
//将用户从 等待 rank 的队列中删除
{
std::unique_lock _lock(_rankLock);
int rank = _users[s->getUserName()]->getRank();
auto it = _rankQueue.find(rank);
if(it != _rankQueue.end())
{
printf("队列踢出测试\n");
_rankQueue.erase(it);
}
}
std::unique_lock _lock(_userlock);
std::map::iterator it = _users.begin();
while(it != _users.end())
{
if(it->second->getSocket() == s)
{
sprintf(sql,"update user set rank = '%d' where name = '%s'",it->second->getRank(),it->second->getName()); //更新段位
_db->db_exec(sql);
printf("移除在线用户测试\n");
_users.erase(it);//移除在线用户
_log->info("用户{}[{}:{}] loginout ",it->second->getName(),s->getIp(),s->getPort());
//释放user
delete it->second;
return;
}
it++;
}
}
}
//发送数据
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[100] = {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;
_log->error("Register select user error");
}
if(outJson.isMember("name"))
{
result = USEREXIST;
}
else
{
sprintf(sql,"insert into user(name,passwd,rank) values('%s','%s',0)",userName.c_str(),passwd.c_str());
bool ret = _db->db_exec(sql);
if(!ret)
{
result = ERROR;
_log->error("Register insert user error");
}
else
{
_log->info("Register user = {} succeed",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 = 0;
//检测用户是否已经注册
char sql[100] = {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;
_log->error("login select user error");
}
if(outJson.isMember("name")) //用户存在表明已经注册过了
{
std::unique_lock lock(_userlock);
if(_users.find(userName) != _users.end()) //说明用户已经登录
{
result = USERLOGIN;
}
else
{
int i = 0;
rank = atoi(outJson["rank"][i].asString().c_str());//字符串转整形
User *user = new User(userName,passwd,rank,s);
_users.insert(make_pair(userName,user));//插入容器
_log->info("用户{}[{}:{}] login ",userName,s->getIp(),s->getPort());
s->setUserName(userName);
}
}
else
{
result = NAMEORPASSWD;
}
Json::Value json;
json["cmd"] = LOGIN;
json["result"] = result;
json["userName"] = userName;
json["rank"] = _rankMap[rank];
writeData(s,json);
}
void myserver::singleGetQuestion(TcpSocket *s)
{
char sql[100] = {0};
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 || outJson["question"].size() != QUESTION_NUM)
{
result = ERROR;
_log->error("login select question error");
}
else
{
Json::Value json;
json["cmd"] = SINGLE_GETQUESTION;
json["result"] = result;
json["question"] = outJson;
_log->info("用户{}[{}:{}]获取题目:{}\n",s->getUserName(),s->getIp(),s->getPort(),json.toStyledString());
writeData(s,json);
}
}
void myserver::startRank(TcpSocket *first,TcpSocket *second)
{
char sql[100] = {0};
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 || outJson["question"].size() != QUESTION_NUM)
{
result = ERROR;
_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;
writeData(first, json); //first是自己 second是对手
//second user
json["enemyName"] = first->getUserName(); //对手的名字
json["enemyRank"] = _rankMap[_users[first->getUserName()]->getRank()]; //对手的分数--段位
json["enemyScore"] = 0;
writeData(second, json);
_log->info("获取 rank 题目 : {}\n",json.toStyledString());
}
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())
{
other = it->second;
_rankQueue.erase(it);
break;
}
it = _rankQueue.find(rank-i);
if(it != _rankQueue.end())
{
other = it->second;
_rankQueue.erase(it);
break;
}
}
}
_log->info(" rank 分数 : {}\n",rank);
if(other == NULL)//如果没有找到匹配的用户 将他加入等候队列
{
_rankQueue.insert(std::make_pair(rank, s));
_log->info("当前等侯 rank 人数 : {}\n",_rankQueue.size());
}
else //如果找到 开始对决 进行发题
{
startRank(s,other);
}
}
void myserver::rankAnswerOneQuestion(TcpSocket *,const Json::Value &inJson)
{
std::string enemyName = inJson["enemyName"].asString();
User *user = _users[enemyName];
Json::Value json;
json["cmd"] = ANSWER;
json["enemyscore"] = inJson["score"].asInt();
json["enemyQuestionId"] = inJson["questionId"].asInt();
writeData(user->getSocket(),json);
}
void myserver::initRankMap()
{
char buf[100] = {0};
int rank = 0;
int num = 0;
for(int i = 0; i < 100;i++)
{
if(i < 9)
{
rank = i / 3;
num = i % 3;
sprintf(buf,"青铜%d %d颗星",3-rank,num +1);
}
else if(9 <= i && i < 18)
{
rank = (i-9) / 3;
num = (i-9) % 3;
sprintf(buf,"白银%d %d颗星",3-rank,num+1);
}
else if(18 <= i && i < 34)
{
rank = (i-18) / 4;
num = (i-18) % 4;
sprintf(buf,"黄金%d %d颗星",4-rank,num+1);
}
else if(34 <= i && i < 50)
{
rank = (i-34) / 4;
num = (i-34) % 4;
sprintf(buf,"铂金%d %d颗星",4-rank,num+1);
}
else if(50 <= i && i < 75)
{
rank = (i-50) / 5;
num = (i-50) % 5;
sprintf(buf,"钻石%d %d颗星",5-rank,num+1);
}
else if(75 <= i && i < 100)
{
rank = (i-75) / 5;
num = (i-75) % 5;
sprintf(buf,"星耀%d %d颗星",5-rank,num+1);
}
_rankMap.insert(std::make_pair(i,buf));
}
/*
for(int i = 0; i < 100; i++)
{
std::cout< lcok(_userlock);
User *user = _users[s->getUserName()];
printf("用户段位%s:%d\n",user->getName(),user->getRank());
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);
}
printf("用户段位%s:%d\n",user->getName(),user->getRank());
Json::Value json;
json["cmd"] = RANKRESULT;
json["newRank"] = _rankMap[user->getRank()];
writeData(s,json);
}
#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("could not initialize libevent\n");
exit(-1);
}
}
//有客户端连接时调用该函数 fd是与客户端通信的描述符
void Tcpserver::listenCb(struct evconnlistener *,evutil_socket_t fd,struct sockaddr*clientAdd,int ,void *data)
{
//printf("有客户端请求链接\n");
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); //该bufferevent监听fd 下面调用相应的函数
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);//设置回调函数函数 对应客户端发来什么请求调用相应函数
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);
}
//监听客户端的链接 有客户端链接的时调用listenCb
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;//成功返回0 失败返回-1
}
void Tcpserver::start()
{
event_base_dispatch(m_base);
evconnlistener_free(m_listener);
event_base_free(m_base);
printf("done\n");
}
#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;
}
//可读事件回调函数 当buffrevent 发来读或者写信号 调用下面的回调函数 回调函数又调用相应的函数进行处理
void TcpSocket::readEventCb(struct bufferevent *,void *ctx)
{
TcpSocket *s = (TcpSocket *)ctx;
m_tcpServer->readEvent(s); //调用readevent对发来的json数据包进行解析读取
}
//可写事件回调函数
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;
}
#include "thread.h"
Thread::Thread()
{
m_base = event_base_new();
if(!m_base)
{
printf("Couldn't creat 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];
//让管道事件监听管道的读段
//如果监听到管道的读段有数据可读 可读事件 永久有效 回调函数
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("线程运行开始测试\n");
//监听base事件集合
event_base_dispatch(m_base);//循环监听 处理事件
event_base_free(m_base);
//printf("线程运行结束测试\n");
return;
}
struct event_base*Thread::getBase()
{
return m_base;
}
#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;
}
void User::setRank(int rank)
{
if(rank <= 0)
rank = 0;
_rank = rank;
}
#ifndef BRAINSTORM_H
#define BRAINSTORM_H
#include
#include "communicate.h"
#include
#include
#include
#include
#include
#include "../common.h"
namespace Ui {
class BrainStorm;
}
class BrainStorm : public QDialog
{
Q_OBJECT
public:
explicit BrainStorm(Communicate *_com,QJsonObject &json,QWidget *parent = 0);
~BrainStorm();
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);
private slots:
void on_singleButton_clicked();
void on_single_back_clicked();
void on_single_start_clicked();
//单人训练从服务器接收到题目
void receiveSingleQuestion(QJsonObject json);
void singleTimerOut();
void on_pushButton_clicked();
void on_singleSelectButton1_clicked();
void on_singleSelectButton2_clicked();
void on_singleSelectButton3_clicked();
void on_singleSelectButton4_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_2_clicked();
private:
Ui::BrainStorm *ui;
Communicate * _com;//通信类指针负责和服务器通信
QJsonObject _singleQuestion; //单人训练问题
int _currentSingleQuestion; //当前回答哪一个问题的下标
QTimer _singleTimer; //个人训练定时器
int _singleSec; //个人训练答题时间
int _singleScore; //个人训练得分
//-------------------------------------rank---------------
QJsonObject _rankQuestion; //rank的题目
QString _enemyName; //对手的名字
QString _enemyRank; //对手的段位
int _enemyScore; //对手的得分
int _rankSec; //rank答题计时
int _myScore; //自己的得分
QString _userName; //自己的名字
QString _userRank; //自己的段位
int _currentRankQuestion; //当前回答哪一个问题的下标
int _enemyRankQuestion; //对手当前回答问题的下标
QTimer _rankTimer; //rank的定时器
};
#endif // BRAINSTORM_H
#ifndef COMMUNICATE_H
#define COMMUNICATE_H
#include
#include
#include
#include
#include
#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 json); //接受个人训练题目
void Rank(QJsonObject json); //发送排位信号
public slots:
void readData();
private:
QTcpSocket s;
};
#endif // COMMUNICATE_H
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include "communicate.h"
#include "../common.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_LoginButton_clicked();
void on_EnrollButton_clicked();
void login(QJsonObject json);
private:
Ui::Widget *ui;
Communicate * _com;//通信类指针负责和服务器通信
};
#endif // WIDGET_H
#ifndef REGISTER_H
#define REGISTER_H
#include
#include
#include "communicate.h"
#include "../common.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_pushButton_2_clicked();
void rstResult(int );
private:
Ui::Register *ui;
Communicate *_com;
};
#endif // REGISTER_H
#include "brainstorm.h"
#include "ui_brainstorm.h"
#include
BrainStorm::BrainStorm(Communicate *com, QJsonObject &json, QWidget *parent):
QDialog(parent),
ui(new Ui::BrainStorm)
{
ui->setupUi(this);
_com = com;
//ui->mainMenu->show();
_userName = json["userName"].toString();
_userRank = json["rank"].toString();
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)));
connect(&_rankTimer,SIGNAL(timeout()),this,SLOT(rankTimerOut()));
this->setWindowIcon(QIcon("D:\\Qmake\\Picture\\pk.jpg"));
this->setWindowTitle("答题");
QMovie *movie = new QMovie ("D:\\Qmake\\Picture\\111.gif" );
ui->background->setMovie(movie);
movie->start() ;
ui->background->setScaledContents(true);
ui->rankButton->setStyleSheet("background-color: rgb(0, 170, 255);");
ui->singleButton->setStyleSheet("background-color: rgb(0, 170, 255);");
QMovie *movie4 = new QMovie ("D:\\Qmake\\Picture\\R-C (2).gif" );
ui->label_4->setMovie(movie4);
movie4->start() ;
ui->label_4->setScaledContents(true);
ui->rank_wait->setStyleSheet("background-color: rgb(252, 252, 252);");
ui->label_5->setPixmap(QPixmap("D:\\Qmake\\Picture\\R-C.jpg"));//QIcon
ui->label_5->setScaledContents(true);
}
BrainStorm::~BrainStorm()
{
delete ui;
}
//个人训练 计时器
void BrainStorm::singleTimerOut()
{
--_singleSec;
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::singleGetQuestion()
{
QJsonObject json;
json["cmd"] = SINGLE_GETQUESTION;
_com->writeData(json);
}
//进入个人训练模式
void BrainStorm::on_singleButton_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->single_menu);
}
//返回上一个界面
void BrainStorm::on_single_back_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
//开始个人答题
void BrainStorm::on_single_start_clicked()
{
singleGetQuestion();
}
//设置个人训练问题
void BrainStorm::singleSetQuestion()
{ //问题
ui->singlequestion->setText(_singleQuestion["question"].toArray().at(_currentSingleQuestion).toString());
//选项
ui->singleSelectButton1->setText(_singleQuestion["selection1"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton2->setText(_singleQuestion["selection2"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton3->setText(_singleQuestion["selection3"].toArray().at(_currentSingleQuestion).toString());
ui->singleSelectButton4->setText(_singleQuestion["selection4"].toArray().at(_currentSingleQuestion).toString());
}
//接受 题目
void BrainStorm::receiveSingleQuestion(QJsonObject json)
{
_singleQuestion = json;
_singleSec = 10;
_currentSingleQuestion = 0;//当前题号为0
_singleScore = 0;
ui->lcdNumber->display(_singleSec);
singleSetQuestion();
//开启定时器
_singleTimer.start(1000);
ui->stackedWidget->setCurrentWidget(ui->single_runing);
}
//答题结束 得分界面返回
void BrainStorm::on_pushButton_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
//个人训练答题
void BrainStorm::singleAnswerQuestion(int select)
{ //回答正确
if(select == _singleQuestion["answer"].toArray().at(_currentSingleQuestion).toString().toInt())
{
_singleScore += 20; //根据剩余时间来打分 每题满分20
_currentSingleQuestion++; //题目下标++
singleSetQuestion(); //设置下一题
_singleSec = 10;
_singleTimer.stop();
ui->lcdNumber->display(_singleSec);
_singleTimer.start(1000);
}
else//回答错误
{
ui->singleResult->setText("回答错误");
QString str = QString("本次得分,%1").arg(_singleScore);
ui->singleScore->setText(str);
ui->stackedWidget->setCurrentWidget(ui->single_score);
_singleTimer.stop();
}
//题目答完
if(_currentSingleQuestion == QUESTION_NUM)
{
ui->singleResult->setText("恭喜全部回答正确");
QString str = QString("本次得分,%1").arg(_singleScore);
ui->singleScore->setText(str);
ui->stackedWidget->setCurrentWidget(ui->single_score);
_singleTimer.stop();
}
}
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->enemyStatus->setText(str);
}
void BrainStorm::setSelfScore()
{
QString str = QString("%1(%2): %3").arg(_userName,-5).arg(_userRank).arg(_myScore);
ui->selfStatus->setText(str);
}
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"] = _myScore;
json["enemyscore"] = _enemyScore;
_com->writeData(json);
}
}
void BrainStorm::rankAnswerQuestion(int select)
{
//计算得分
if(select == _rankQuestion["answer"].toArray().at(_currentRankQuestion).toString().toInt())
_myScore += 20;
setSelfScore();
_currentRankQuestion++;
//判断是否跳到下一题
if(_currentRankQuestion == _enemyRankQuestion)//自己和对象的问题下标相等则进入下一题
{
_rankSec = 10;
_rankTimer.stop();
ui->lcdNumber_2->display(_rankSec);
_rankTimer.start(1000);
rankSetQuestion();
}
QJsonObject json;
json["cmd"] = ANSWER;
json["enemyName"] = _enemyName;
json["score"] = _myScore;
json["questionId"] = _currentRankQuestion;
_com->writeData(json);
}
void BrainStorm::rankSetResult(QJsonObject &json)
{
QString newRank = json["newRank"].toString();
if(_myScore == _enemyScore)
{
ui->rankResult->setText("平局");
}
else if(_myScore < _enemyScore)
{
ui->rankResult->setText("失败");
}
else if(_myScore > _enemyScore)
{
ui->rankResult->setText("胜利");
}
QString str = QString("%1 ---> %2").arg(_userRank).arg(newRank);
ui->newRank->setText(str);
_userRank = newRank;
ui->stackedWidget->setCurrentWidget(ui->rank_result);
std::cout<< "Rankresult"<lcdNumber_2->display(_rankSec);
_currentRankQuestion= 0;
_enemyRankQuestion = 0;
setEnemyScore();
setSelfScore();
rankSetQuestion();
_rankTimer.start(1000);
ui->stackedWidget->setCurrentWidget(ui->rank_running);
}
else if(cmd == ANSWER)
{
_enemyScore = json["enemyscore"].toInt();
_enemyRankQuestion = json["enemyQuestionId"].toInt();
setEnemyScore();
if(_currentRankQuestion ==_enemyRankQuestion)
{
_rankSec = 10;
_rankTimer.stop();
ui->lcdNumber_2->display(_rankSec);
_rankTimer.start(1000);
rankSetQuestion();
}
}
else if(cmd == RANKRESULT)
{
rankSetResult(json);
}
}
void BrainStorm::on_rankSelectButton1_clicked()
{
ui->rankSelectButton1->setStyleSheet("background-color: rgb(255, 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(255, 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(255, 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(255, 170, 255);");
ui->rankSelectButton1->setEnabled(false);
ui->rankSelectButton2->setEnabled(false);
ui->rankSelectButton3->setEnabled(false);
ui->rankSelectButton4->setEnabled(false);
rankAnswerQuestion(4);
}
void BrainStorm::on_pushButton_2_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->mainMenu);
}
#include "communicate.h"
#include
#include
Communicate::Communicate(QObject *parent) : QObject(parent)
{
//连接服务器
s.connectToHost(QHostAddress("192.168.90.82"),8787);
connect(&s,SIGNAL(readyRead()),this,SLOT(readData()));
}
//如果服务器给客户端发消息
void Communicate::readData()
{
QByteArray data; //字节数组
while(s.bytesAvailable())
{
data += s.readAll();
}
//数据解析 解析成json字符串
QJsonDocument dt = QJsonDocument::fromJson(data);
if(dt.isNull())
{
return ;
}
QJsonObject root = dt.object();
//具体的逻辑处理
int cmd = root["cmd"].toInt();
qDebug()<<"cmd:"<
#include "login.h"
#include "ui_widget.h"
#include "register.h"
#include
#include "brainstorm.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)));
this->setWindowIcon(QIcon("D:\\Qmake\\Picture\\pk.jpg"));
this->setWindowTitle("登录");
this->setStyleSheet("backgrount:gray");
QPalette pal =this->palette();
pal.setBrush(QPalette::Background,QBrush(QPixmap("D:\\Qmake\\Picture\\background.png")));
setPalette(pal);
ui->EnrollButton->setStyleSheet("background-color: rgb(0, 170, 255);");
ui->LoginButton->setStyleSheet("background-color: rgb(0, 170, 255);");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_LoginButton_clicked()
{
QString userName = ui->UsrNameEdit->text();
QString passWd = ui->PassWordlineEdit->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);
break;
}
case ERROR:
QMessageBox::information(this,"登录","登录失败,未知错误");
break;
case USERLOGIN:
QMessageBox::information(this,"登录","登录失败,不接重复登录");
break;
case NAMEORPASSWD:
QMessageBox::information(this,"登录","用户名或者密码错误");
break;
default:
break;
}
}
void Widget::on_EnrollButton_clicked()
{
this->hide();
Register reg(_com);
reg.exec();
this->show();
}
#include "login.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "register.h"
#include "ui_register.h"
#include
Register::Register(Communicate *com,QWidget *parent) :
QDialog(parent),
ui(new Ui::Register)
{
ui->setupUi(this);
this->setWindowTitle("注册");
_com =com;
connect(_com,SIGNAL(rstResult(int)),this,SLOT(rstResult(int)));
}
Register::~Register()
{
delete ui;
}
void Register::on_RegisterButton_clicked()
{
QString userName = ui->UsrNamelineEdit->text();
QString passWd = ui->PasswordlineEdit->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::information(this,"注册","注册失败,未知错误");
break;
case USEREXIST:
QMessageBox::information(this,"注册","用户已经存在");
break;
default:
break;
}
}
void Register::on_pushButton_2_clicked()
{
close();
}