目录

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


  1. 开发环境/工具: Linux,QT,libevent库,Mysql数据库,jsoncpp库。
  2. 基于 Qt 平台:分别在 windows 和 Linux 下构建客户端和服务器,实现“头脑 风暴”                            在线对战小游戏。
  3. 实现功能:实现了用户注册、用户登录、排位匹配对战答题、个人训练答题、段位记                      录系统、段位匹配系统等功能。

Server服务器端

Server.pro

补上需要的代码:


CONFIG += console c++11#jsoncpp
LIBS +=-L/usr/local/lib -levent -lpthread -lmysqlclient -ljson

tcpserver.h

#ifndef TCPSERVER_H
#define TCPSERVER_H
#include"thread.h"
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.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 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

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_compat.h>
#include<event2/event_struct.h>
class Thread
{
public:Thread();void start();//线程运行//获取事件集合struct event_base *getBase();protected://在类的内部调用回调函数就要变成静态,加staticstatic void* worker(void*);//线程的工作函数static void pipeRead(evutil_socket_t,short,void *);//管道可读void run();            //线程的逻辑处理函数private:struct event_base *m_base;//事件集合pthread_t m_threadId;     //线程IDint m_pipeReadFd;        //管道的读端int m_pipeWriteFd;       //管道的写端struct event m_pipeEvent;   //管道事件};#endif // THREAD_H

tcpsocket.h

#ifndef TCPSOCKET_H
#define TCPSOCKET_H#include"tcpserver.h"
#include<string>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

db.h

#ifndef DB_H
#define DB_H
#include"mysql/mysql.h"
#include<mutex>
#include<spdlog/spdlog.h>
#include<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

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();void setRank(int rank);private:std::string _userName;  //用户名std::string _passwd;    //用户密码int _rank;             //rank分数TcpSocket *_s;          //通信类套接字
};#endif // USER_H

myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H#include"tcpserver.h"
#include<spdlog/spdlog.h>
#include"db.h"
#include<json/json.h>
#include<json/reader.h>
#include"../common.h"
#include<string>
#include<map>
#include"user.h"
#define DEBUG
//#define QUESTION_NUM 5class MyServer:public TcpServer//继承
{
public:MyServer();void connectEvent(TcpSocket *);void readEvent(TcpSocket *);void writeEvent(TcpSocket *);void closeEvent(TcpSocket *s, 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 get_SingleQuestion(TcpSocket *s);//进行排位void Rank(TcpSocket *s);//rank 回答一道问题void rankAnswerOneQuestion(TcpSocket *s,const Json::Value &inJson);//开始对决void startRank(TcpSocket *first,TcpSocket *second);//rank结果void rankResult(TcpSocket *s, const Json::Value &inJson);//初始化rank积分对照表void initRankMap();private:std::shared_ptr<spdlog::logger> _log;//记录日志的句柄DB* _db;//数据库句柄//键是用户名,值是用户指针std::mutex _userLock;//锁_Usersstd::map<std::string,User*> _users;    //在线用户列表//key : rank积分//值  : 对应的段位说明std::map<int,std::string> _rankMap;    //rank积分比对表//key   : rank积分//value : 参与rank的用户socket句柄std::mutex _rankLock;std::map<int,TcpSocket*> _rankQueue;   //等待排位的队列
};#endif // MYSERVER_H

---------------------------------------------------------------------------------------------------------------------------------

tcpserver.cpp

#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监测fdstruct 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<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");
}

thread.cpp

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

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

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 *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<std::mutex> 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<std::mutex> 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<num_fields;i++){outJson[fetch_field[i].name].append(row[i]);}}//获取完把结构给删掉mysql_free_result(sql_res);return true;
}

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;
}void User::setRank(int rank)
{if(rank<=0)rank=0;_rank=rank;
}

myserver.cpp

#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<std::mutex> _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<std::mutex> _lock(_userLock);std::map<std::string,User*>::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;//移除完之后释放Userdelete 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<std::mutex> _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);}}elseresult=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 userjson ["enemyName"] =second->getUserName();json ["enemyRank"] =_rankMap[_users[second->getUserName()]->getRank()];json ["enemyScore"]=0;writeData(first,json);//second userjson ["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<std::mutex> lock(_rankLock);//查找同一段位的对手std::map<int,TcpSocket*>::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<std::mutex> lock(_userLock);User *user=_users[s->getUserName()];int score     =inJson["score"].asInt();int enemyscore=inJson["enemyscore"].asInt();if(score<enemyscore)user->setRank(user->getRank()-1);else if(score>enemyscore)user->setRank(user->getRank()+1);Json::Value json;json ["cmd"]     =RANKRESULT;json ["newRank"] =_rankMap[user->getRank()];writeData(s,json);//发送给客户端
}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<<i<<":"<<_rankMap[i]<<std::endl;}*/}

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("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;
}

Client客户端

Client.pro

补上需要的代码:

QT       += core gui network

register.h

#ifndef REGISTER_H
#define REGISTER_H#include <QDialog>
#include<QJsonObject>
#include"communicate.h"
#include"../common.h"namespace Ui {
class Register;
}class Register : public QDialog
{Q_OBJECTpublic: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

login.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include"register.h"
#include"communicate.h"namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic: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

communicate.h

#ifndef COMMUNICATE_H
#define COMMUNICATE_H#include <QObject>
#include<QTcpSocket>
#include<QJsonObject>
#include<QJsonDocument>
#include"../common.h"
#include<QJsonArray>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

brainstorm.h

#ifndef BRAINSTORM_H
#define BRAINSTORM_H#include <QDialog>
#include"communicate.h"
#include<QJsonObject>
#include<QTimer>namespace Ui {
class BrainStorm;
}class BrainStorm : public QDialog
{Q_OBJECTpublic: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

---------------------------------------------------------------------------------------------------------------------------------

register.cpp

#include "register.h"
#include "ui_register.h"
#include<QMessageBox>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();
}

login.cpp

#include "login.h"
#include "ui_widget.h"
#include"register.h"
#include<QMessageBox>
#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();
}

communicate.cpp

#include "communicate.h"
#include<QHostAddress>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);//发送数据
}

brainstorm.cpp

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

main.cpp

#include "login.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

基于Qt的在线抢答系统相关推荐

  1. 基于JavaWeb实现在线租房系统

    ​​​​​​​ 作者主页:编程指南针 简介:Java领域优质创作者.CSDN博客专家  Java项目.简历模板.学习资料.面试题库.技术互助 文末获取源码 项目编号:BS-PT-043 项目运行环境: ...

  2. 基于HTML在线考试系统开题报告,基于JSP的在线考试系统 开题报告.doc

    基于JSP的在线考试系统 开题报告 毕业设计(论文)开题报告 论文题目: 基于JSP的在线考试系统 Online Examination System Based on JSP on JSP 学 生 ...

  3. pop客户机程序流程图_基于.NET的在线考试系统的设计与实现

    基于.NET的在线考试系统的设计与实现(包含任务书,开题报告,毕业论文19000字,程序代码,数据库) 摘  要 随着互联网的发展,人们已经进入了信息时代.在这种环境下,学生希望得到个性化的满足,这使 ...

  4. 基于html5的在线教育,基于HTML5的在线学习系统的设计与实现

    基于HTML5的在线学习系统的设计与实现 发布时间:2019-11-18所属分类:科技论文浏览:1次 摘 要: 摘 要: 在线课程学习网站的发展迅速,吸引了广大用户.基于 HTML5 的在线学习系统经 ...

  5. 基于JAVA彩票在线购买系统计算机毕业设计源码+系统+lw文档+部署

    基于JAVA彩票在线购买系统计算机毕业设计源码+系统+lw文档+部署 技术栈 项目架构:B/S架构 开发语言:Java语言 开发软件:idea eclipse 前端技术:Layui.HTML.CSS. ...

  6. JOJ——基于爬虫的在线测评系统(Online Judge)

    这是一个基于爬虫的在线测评系统(OJ). 相信喜欢刷题的各位大佬应该对OJ并不陌生.本系统旨在使用一个账号,就可以刷遍各个OJ的题目. 系统基于后端SpringBoot.Mybatis-Plus.Th ...

  7. Linux下基于qt的视频监控系统

    目录 一.原始需求 二.环境安装 2.1 qt安装 2.2 opencv安装 三.系统设计 3.1. 整体流程设计 3.2 .数据传输交互流程 3.3 .数据库设计 四.关键代码 4.1.如何实现通信 ...

  8. 可视化编程,公网访问——全网首个基于Node-red的在线评语系统

    本文内容已迁移,新的阅读地址可视化编程,公网访问--全网首个基于Node-red的在线评语系统 更新更全的Node-RED教程,请访问 Node-RED系列教程

  9. 基于web的在线考试系统的设计与开发

    欢迎添加微信互相交流学习哦! 项目源码:https://gitee.com/oklongmm/biye2 在线考试系统的设计与开发 目录 TOC 1-3 第一章 绪论 1 1.1在线考试系统的研究背景 ...

  10. 基于QT的网络视频会议系统---KNVM

    本文来自http://blog.csdn.net/slientman原创,引用必须注明出处! KNVM网络视频会议系统V1.0 经过2年艰苦细致的开发,终于完成了基于QT的网络视频会议系统,特此记录, ...

最新文章

  1. 【网页前端设计Front end】JavaScript教程.上(看不懂你来打我)
  2. linux下使用expect+scp+shell实现分布式集群系统安装,升级,部署
  3. fdtd中时间监视器怎么放_FDTDsolutions经典问题集.pdf
  4. python如何使用多线程_python多线程应用中的详细介绍
  5. css3怎么设置logo,纯CSS3实现的LOGO标志 ABN AMRO CSS3 logo
  6. 相机标定(3) opencv中solvePnPRansac()和solvePnP()计算外参数
  7. Codeforces - 706B - Interesting drink - 二分 - 简单dp
  8. 分别用邻接矩阵和邻接表实现图的深度优先遍历和广度优先遍历_数据结构与算法:三十张图弄懂「图的两种遍历方式」...
  9. Webpack 学习记录
  10. C# pdf转jpg文件
  11. 液晶屏字模提取软件问题总结·
  12. efi分区能不能删除 win10_Win10不小心把EFI分区删除怎么恢复?
  13. 【论文速览】PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection
  14. HTML七大选择器,HTML各类选择器
  15. (什么是 RPC?) AND(什么是域名?)AND (DMA是什么?)AND(IRQ是什么?) 选择自 ycool1984 的 Blog
  16. 前端维护项目该怎么做呢
  17. 俄罗斯方块30周年 设计者忆当年
  18. 中望CAD的lisp编辑器_巧用中望CAD2017自定义工具选项板
  19. 快手616品质购物节电商报告来了!24位达人带货破亿,新一代带货王诞生!
  20. 不懂SpringApplication生命周期事件?那就等于不会Spring Boot嘛

热门文章

  1. matlab 提取子波,地震子波提取研究
  2. vs2012窗体嵌入表格_2012年电子表格日
  3. 监控和路由器不在同一个ip段_如何把二级路由器当交换机使用
  4. mysql身份证来算年龄_MySQL 根据身份证出生年月计算年龄户籍地性别
  5. 32年魔咒破解,见证国足3:0大胜韩国
  6. FusionCharts学习总结
  7. 解决linux 连接网络但不能上网问题
  8. 串口通信基础(一)——串行与并行通信,同步与异步通信
  9. 大四学年个人自我鉴定
  10. 转 | pll loop的tran与stb仿真