目录
Server客户端
Server.pro
tcpserver.h
thread.h
tcpsocket.h
db.h
user.h
myserver.h
------------------
tcpserver.cpp
thread.cpp
tcpsocket.cpp
db.cpp
user.cpp
myserver.cpp
main.cpp
Client客户端
Client.pro
register.h
login.h
communicate.h
brainstorm.h
------------------
register.cpp
login.cpp
communicate.cpp
brainstorm.cpp
main.cpp
- 开发环境/工具: Linux,QT,libevent库,Mysql数据库,jsoncpp库。
- 基于 Qt 平台:分别在 windows 和 Linux 下构建客户端和服务器,实现“头脑 风暴” 在线对战小游戏。
- 实现功能:实现了用户注册、用户登录、排位匹配对战答题、个人训练答题、段位记 录系统、段位匹配系统等功能。
补上需要的代码:
CONFIG += console c++11
#jsoncpp
LIBS +=-L/usr/local/lib -levent -lpthread -lmysqlclient -ljson
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include"thread.h"
#include
#include
#include
#include
#include
#include
#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 fd, struct sockaddr *clientAdd, int socklen, void *data);
//监听处理函数
void listenEvent(evutil_socket_t fd, struct sockaddr_in *clientAdd);
//--------虚函数,去具体处理客户端的逻辑--------
//客户端连接事件
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 THREAD_H
#define THREAD_H
#include
#include
#include
#include
#include
#include
#include
class Thread
{
public:
Thread();
void start();//线程运行
//获取事件集合
struct event_base *getBase();
protected:
//在类的内部调用回调函数就要变成静态,加static
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
#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 *etc);
//可写事件的回调函数
static void writeEventCb(struct bufferevent *bev,void *etc);
//异常事件的回调函数
static void closeEventCb(struct bufferevent *bev,short what,void *etc);
char *getIp();//获取IP地址
u_int16_t getPort();//获取端口
//从客户端读数据
int readDate(void *data, int size);
//向客户端写数据
int writeDate(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 DB_H
#define DB_H
#include"mysql/mysql.h"
#include
#include
#include
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
#ifndef USER_H
#define USER_H
#include
#include"tcpsocket.h"
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
#ifndef MYSERVER_H
#define MYSERVER_H
#include"tcpserver.h"
#include
#include"db.h"
#include
#include
#include"../common.h"
#include
#include
---------------------------------------------------------------------------------------------------------------------------------
#include "tcpserver.h"
TcpServer::TcpServer(int threadNum):m_nextThread(0)//下标初始化为0
{
if(threadNum<=0)
{
printf("threadNum<=0");
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);
}
}
//evutil_socket_t fd: 与客户端通信的文件描述符
//struct sockaddr *clientAdd: 客户端的IP地址,地址信息。
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 *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;
//base监测fd
struct bufferevent *bev=bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);
if(!bev)
{
fprintf(stderr,"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);
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
#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("fork");
exit(-1);
}
//管道的读写端
m_pipeReadFd=fd[0];
m_pipeWriteFd=fd[1];
//让管道事件监听管道的读端
//如果监听到,管道的读端有数据可读
//EV_READ:监听管道是否有可读事件
//EV_PERSIST:让事件永久有效,不然读完一次下次就不监听了
//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(int, 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()
{
/*测试用的
while(1)
{
printf("thread:%d\n",m_threadId);
sleep(2);
}
*/
//printf("%d:start\n",m_threadId);
//监听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;
}
#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 *etc)
{
TcpSocket *s=(TcpSocket*)etc;
m_tcpServer->readEvent(s);
}
//可写事件的回调函数
void TcpSocket::writeEventCb(struct bufferevent *,void *etc)
{
TcpSocket *s=(TcpSocket*)etc;
m_tcpServer->writeEvent(s);
}
//异常事件的回调函数
void TcpSocket::closeEventCb(struct bufferevent *,short what,void *etc)
{
TcpSocket *s=(TcpSocket*)etc;
m_tcpServer->closeEvent(s,what);
delete s;
}
char *TcpSocket::getIp()
{
return m_ip;
}
u_int16_t TcpSocket::getPort()
{
return m_port;
}
//从客户端读数据
int TcpSocket::readDate(void *data,int size)
{
return bufferevent_read(m_bev,data,size);
}
//向客户端写数据
int TcpSocket::writeDate(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 "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 *con=mysql_real_connect(_mysql,host,userName,passwd,dbName,0,NULL,0);
if(con==NULL)
{
spdlog::get("BrainStorm")->error("连接数据库失败:{}",mysql_error(_mysql));
exit(-1);
}
_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;
}
//从mysql服务器下载查询结果
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_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
#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;
}
#include "myserver.h"
MyServer::MyServer()
{
//日志
#ifdef DEBUG//向控制台写
_log=spdlog::stdout_color_mt("console");
#else
_log=spdlog::rotating_logger_mt("BrainStorm","BrainStorm",1024*1024*5,3);
_log->flush_on(spdlog::level::info);
#endif
//_log->info("11111111111");//写在控制台
_db=new DB(NULL,"xiaowang","xiaowang","BrainStorm");
initRankMap();
}
void MyServer::connectEvent(TcpSocket *s)
{
_log->info("有一个新的连接[{}:{}]\n",s->getIp(),s->getPort());
//printf("客户端连接%s","端口连接%s",s->getIp(),s->getPort());
}
void MyServer::readEvent(TcpSocket *s)
{
char buf[1024]={0};
while(1)
{
int len=0;
s->readDate(&len,sizeof(len));
if(len<=0)
break;
s->readDate(buf,len);
//数据解析
Json::Value root;
Json::Reader reader;//json解析器
if(!reader.parse(buf,root))
{
_log->error("json数据解析失败");
return;
}
int cmd=root["cmd"].asInt();
switch (cmd)
{
case REGISTER:
Register(s,root);
break;
case LOGIN:
Login(s,root);
break;
case SINGLE_QUESTION:
get_SingleQuestion(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)
{
//将用户从 等待 rank 的列表中删除
{
std::unique_lock _lock(_rankLock);
int rank=_users[s->getUserName()]->getRank();
auto it=_rankQueue.find(rank);//然后从队列里面去找他
if(it != _rankQueue.end())
_rankQueue.erase(it);
}
//printf("客户端退出,ip:%s,端口:%d\n",s->getIp(),s->getPort());
//_log->error("用户{}[{}:{}] logout",userName,s->getIp(),s->getPort());
std::unique_lock _lock(_userLock);
std::map::iterator it=_users.begin();
while(it!=_users.end())
{
if(it->second->getSocket()==s)
{
_users.erase(it);
_log->info("用户{}[{}:{}] logout",it->second->getName(),s->getIp(),s->getPort());
break;
//移除完之后释放User
delete it->second;
return;
}
it++;
}
_log->info("用户{}[{}:{}] logout",s->getIp(),s->getPort());
}
void MyServer::writeData(TcpSocket *s,const Json::Value &inJson)
{
std::string data=inJson.toStyledString();
s->writeDate(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));//用户创建完,把他放进列表里,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::get_SingleQuestion(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("get_SingleQuestion select question error");
}
Json::Value json;
json ["cmd"] =SINGLE_QUESTION;
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);
//second user
json ["enemyName"] =first->getUserName();
json ["enemyRank"] =_rankMap[_users[first->getUserName()]->getRank()];
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=0;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 分数:{}",rank);
if(other==NULL)//没有匹配到用户
{
_rankQueue.insert(std::make_pair(rank,s));
_log->info("当前等候 rank 人数:{}",_rankQueue.size());
}
else//找到
{
//开始对决
startRank(s,other);
}
}
void MyServer::rankAnswerOneQuestion(TcpSocket *s,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::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(scoresetRank(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);//发送给客户端
}
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(9<=i && i<18)
{
rank=(i-9)/4;
num=(i-9)%4;
sprintf(buf,"黄金%d %d颗星",4-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)/5;
num=(i-34)%5;
sprintf(buf,"钻石%d %d颗星",5-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);
}
_rankMap.insert(std::make_pair(i,buf));
}
/*
for(int i=0;i<100;i++)
{
std::cout<
#include
#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("1111\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;
}
补上需要的代码:
QT += core gui network
#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_RegNButton_clicked();
void on_BackButton_clicked();
void rstResult(int);
private:
Ui::Register *ui;
Communicate *_com;
};
#endif // REGISTER_H
#ifndef WIDGET_H
#define WIDGET_H
#include
#include"register.h"
#include"communicate.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_LogButton_clicked();
void on_RegButton_clicked();
void login(QJsonObject json);
private:
Ui::Widget *ui;
Communicate *_com;//通信类指针,负责和服务器通信
};
#endif // WIDGET_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 json);
void Rank(QJsonObject json);
public slots:
void readData();
private:
QTcpSocket s;
};
#endif // COMMUNICATE_H
#ifndef BRAINSTORM_H
#define BRAINSTORM_H
#include
#include"communicate.h"
#include
#include
namespace Ui {
class BrainStorm;
}
class BrainStorm : public QDialog
{
Q_OBJECT
public:
explicit BrainStorm(Communicate *_com,QJsonObject &json,QWidget *parent = 0);
~BrainStorm();
private:
//单人训练,从服务器获取题目
void getSingleQuestion();
//单人训练设置问题
void SingleSetQuestion();
//单人训练答题
void SingleAnswerQuestion(int select);
//设置得分
void setEnemyScore();
void setSelfScore();
//--------------------------------------------------------
//rank设置问题
void rankSetQuestion();
//rank结果
void rankSetResult(QJsonObject &json);
//rank答题
void RankAnswerQuestion(int select);
private slots:
void on_RankButton_clicked();
void on_SingleButton_clicked();
void on_SingleBack_clicked();
void on_SingleStart_clicked();
//单人训练从服务器接收到题目
void receiveSingleQuestion(QJsonObject json);
//个人训练计时器
void SingleTimerOut();
void on_OverBack_clicked();
void on_SingleButtonA_clicked();
void on_SingleButtonB_clicked();
void on_SingleButtonC_clicked();
void on_SingleButtonD_clicked();
//----------------------------------------------------------------------
//接受排位
void Rank(QJsonObject json);
//排位计时器
void RankTimerOut();
void on_RankButtonA_clicked();
void on_RankButtonB_clicked();
void on_RankButtonC_clicked();
void on_RankButtonD_clicked();
void on_overButton_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; //排位定时器
};
#endif // BRAINSTORM_H
---------------------------------------------------------------------------------------------------------------------------------
#include "register.h"
#include "ui_register.h"
#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_RegNButton_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()
{
close();
}
#include "login.h"
#include "ui_widget.h"
#include"register.h"
#include
#include"brainstorm.h"
#include"../common.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_LogButton_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);//属性,把new出来的自己销毁
break;
}
case ERROR:
QMessageBox::critical(this,"登录","登录失败,未知错误");
break;
case USERLOGIN:
QMessageBox::critical(this,"登录","登录失败,不允许重复登录");
break;
case NAMEORPASSWD:
QMessageBox::critical(this,"登录","用户名或密码错误");
break;
default:
break;
}
}
void Widget::on_RegButton_clicked()
{
this->hide();
Register reg(_com);
reg.exec();
this->show();
}
#include "communicate.h"
#include
Communicate::Communicate(QObject *parent) : QObject(parent)
{
//连接服务器
s.connectToHost(QHostAddress("***.***.***.***"),****);
connect(&s,SIGNAL(readyRead()),this,SLOT(readData()));
}
void Communicate::readData()
{
QByteArray data;
while(s.bytesAvailable())
{
data +=s.readAll();//读完
}
//数据解析
QJsonDocument dt=QJsonDocument::fromJson(data);//通过QJsonDocument转换成json字符串
if(dt.isNull())//没有读到数据
{
return;
}
QJsonObject root=dt.object();//拿到json这样的一个对象
//具体的逻辑处理
int cmd=root["cmd"].toInt();
switch (cmd)
{
case REGISTER:
emit rstResult(root["result"].toInt());//发给注册那一端
break;
case LOGIN:
emit login(root);//发给注册那一端
break;
case SINGLE_QUESTION:
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 d(json);
QByteArray sendData=d.toJson();
int len=sendData.size();
s.write((char *)&len,sizeof(int));//发送数据长度
s.write(sendData);//发送数据
}
#include "brainstorm.h"
#include "ui_brainstorm.h"
BrainStorm::BrainStorm(Communicate *com, QJsonObject &json, QWidget *parent) :
QDialog(parent),
ui(new Ui::BrainStorm)
{
ui->setupUi(this);
_com=com;
_userName=json["userName"].toString();
_userRank=json["rank"].toString();
connect(&_singleTimer,SIGNAL(timeout()),this,SLOT(SingleTimerOut()));
connect(&_rankTimer,SIGNAL(timeout()),this,SLOT(RankTimerOut()));
connect(_com,SIGNAL(receiveSingleQuestion(QJsonObject)),this,SLOT(receiveSingleQuestion(QJsonObject)));
connect(_com,SIGNAL(Rank(QJsonObject)),this,SLOT(Rank(QJsonObject)));
}
BrainStorm::~BrainStorm()
{
delete ui;
}
//进入个人训练赛
void BrainStorm::on_SingleButton_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->SingleMenu);
}
//个人训练计时器
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->SingleScore);
}
ui->lcdNumber->display(_singleSec);
}
//单人训练,从服务器获取题目
void BrainStorm::getSingleQuestion()
{
QJsonObject json;
json["cmd"]=SINGLE_QUESTION;
_com->writeData(json);
}
//单人训练设置问题
void BrainStorm::SingleSetQuestion()
{
//题目结束
if(_currentSingleQuestion==QUESTION_NUM)
{
ui->Singleresult->setText("恭喜全部答对");
QString str=QString("本次得分:%1").arg(_singleScore);
ui->Singlescore->setText(str);
ui->stackedWidget->setCurrentWidget(ui->SingleScore);
_singleTimer.stop();
return;
}
ui->SingleQuestion->setText(_singleQuestion["question"].toArray().at(_currentSingleQuestion).toString());
ui->SingleButtonA->setText(_singleQuestion["selectionA"].toArray().at(_currentSingleQuestion).toString());
ui->SingleButtonB->setText(_singleQuestion["selectionB"].toArray().at(_currentSingleQuestion).toString());
ui->SingleButtonC->setText(_singleQuestion["selectionC"].toArray().at(_currentSingleQuestion).toString());
ui->SingleButtonD->setText(_singleQuestion["selectionD"].toArray().at(_currentSingleQuestion).toString());
}
//单人训练答题
void BrainStorm::SingleAnswerQuestion(int select)
{
//回答正确
if(select==_singleQuestion["answer"].toArray().at(_currentSingleQuestion).toString().toInt())
{
_singleScore +=20*_singleSec;
_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->SingleScore);
_singleTimer.stop();
}
}
//单人训练从服务器接收到题目
void BrainStorm::receiveSingleQuestion(QJsonObject json)
{
_singleQuestion=json;
_singleSec=10;
_currentSingleQuestion=0;
_singleScore=0;
ui->lcdNumber->display(_singleSec);
SingleSetQuestion();
//开启定时器
_singleTimer.start(1000);
ui->stackedWidget->setCurrentWidget(ui->SingleRuning);
}
//从个人训练返回
void BrainStorm::on_SingleBack_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->MainMenu);
}
//个人训练赛开始答题
void BrainStorm::on_SingleStart_clicked()
{
getSingleQuestion();
}
void BrainStorm::on_OverBack_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->MainMenu);
}
void BrainStorm::on_SingleButtonA_clicked()
{
SingleAnswerQuestion(1);
}
void BrainStorm::on_SingleButtonB_clicked()
{
SingleAnswerQuestion(2);
}
void BrainStorm::on_SingleButtonC_clicked()
{
SingleAnswerQuestion(3);
}
void BrainStorm::on_SingleButtonD_clicked()
{
SingleAnswerQuestion(4);
}
//--------------------------------------rank------------------------------------------
//排位赛
void BrainStorm::on_RankButton_clicked()
{
QJsonObject json;
json["cmd"]=RANK;
_com->writeData(json);
//跳到等待页面
ui->stackedWidget->setCurrentWidget(ui->RankWait);
}
void BrainStorm::RankTimerOut()
{
_rankSec--;
if(_rankSec==0)
{
if(ui->RankButtonA->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->RankButtonA->setText(_rankQuestion["selectionA"].toArray().at(_currentRankQuestion).toString());
ui->RankButtonB->setText(_rankQuestion["selectionB"].toArray().at(_currentRankQuestion).toString());
ui->RankButtonC->setText(_rankQuestion["selectionC"].toArray().at(_currentRankQuestion).toString());
ui->RankButtonD->setText(_rankQuestion["selectionD"].toArray().at(_currentRankQuestion).toString());
ui->RankButtonA->setEnabled(true);
ui->RankButtonB->setEnabled(true);
ui->RankButtonC->setEnabled(true);
ui->RankButtonD->setEnabled(true);
ui->RankButtonA->setStyleSheet("");
ui->RankButtonB->setStyleSheet("");
ui->RankButtonC->setStyleSheet("");
ui->RankButtonD->setStyleSheet("");
if(_currentRankQuestion==QUESTION_NUM)
{
_rankTimer.stop();
//将结果发给服务器
QJsonObject json;
json["cmd"] =RANKRESULT;
json["score"] =_myScore;
json["enemyName"] =_enemyName;
json["enemyScore"]=_enemyScore;
_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->RankResult);
}
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=10;
_myScore=0;
ui->lcdNumber_2->display(_rankSec);
_currentRankQuestion=0;
_enemyRankQuestion =0;
setEnemyScore();
setSelfScore();
rankSetQuestion();
_rankTimer.start(1000);
ui->stackedWidget->setCurrentWidget(ui->RankRunning);
}
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::RankAnswerQuestion(int select)
{
//计算得分
if(select==_rankQuestion["answer"].toArray().at(_currentRankQuestion).toString().toInt())
_myScore +=20*_rankSec;
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::on_RankButtonA_clicked()
{
ui->RankButtonA->setStyleSheet("background-color:rgb(255, 0, 0)");
ui->RankButtonA->setEnabled(false);
ui->RankButtonB->setEnabled(false);
ui->RankButtonC->setEnabled(false);
ui->RankButtonD->setEnabled(false);
RankAnswerQuestion(1);
}
void BrainStorm::on_RankButtonB_clicked()
{
ui->RankButtonB->setStyleSheet("background-color:rgb(255, 0, 0)");
ui->RankButtonA->setEnabled(false);
ui->RankButtonB->setEnabled(false);
ui->RankButtonC->setEnabled(false);
ui->RankButtonD->setEnabled(false);
RankAnswerQuestion(2);
}
void BrainStorm::on_RankButtonC_clicked()
{
ui->RankButtonC->setStyleSheet("background-color:rgb(255, 0, 0)");
ui->RankButtonA->setEnabled(false);
ui->RankButtonB->setEnabled(false);
ui->RankButtonC->setEnabled(false);
ui->RankButtonD->setEnabled(false);
RankAnswerQuestion(3);
}
void BrainStorm::on_RankButtonD_clicked()
{
ui->RankButtonD->setStyleSheet("background-color:rgb(255, 0, 0)");
ui->RankButtonA->setEnabled(false);
ui->RankButtonB->setEnabled(false);
ui->RankButtonC->setEnabled(false);
ui->RankButtonD->setEnabled(false);
RankAnswerQuestion(4);
}
void BrainStorm::on_overButton_clicked()
{
ui->stackedWidget->setCurrentWidget(ui->MainMenu);
}
#include "login.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}