基于Qt的在线抢答系统

目录

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

thread.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

tcpsocket.h

#ifndef TCPSOCKET_H
#define TCPSOCKET_H

#include"tcpserver.h"
#include

class TcpServer;

//通信类,负责与客户端进行通信
class TcpSocket
{

public:
    TcpSocket(TcpServer *tcpServer, struct bufferevent *bev, char *ip, u_int16_t port);

    //可读事件的回调函数
    static 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
#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

user.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

myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include"tcpserver.h"
#include
#include"db.h"
#include
#include
#include"../common.h"
#include
#include
#include"user.h"
#define DEBUG
//#define QUESTION_NUM 5

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

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

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

    //key   : rank积分
    //value : 参与rank的用户socket句柄
    std::mutex _rankLock;
    std::map _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监测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

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

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 _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<

main.cpp

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

Client客户端

Client.pro

补上需要的代码:



QT       += core gui network

register.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_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 
#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

communicate.h

#ifndef COMMUNICATE_H
#define COMMUNICATE_H

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

class Communicate : public QObject
{
    Q_OBJECT
public:
    explicit Communicate(QObject *parent = nullptr);
    void writeData(const QJsonObject &json);

signals:
    void rstResult(int );
    void login(QJsonObject json);
    void receiveSingleQuestion(QJsonObject json);

    void Rank(QJsonObject json);

public slots:
    void readData();


private:
    QTcpSocket s;

};

#endif // COMMUNICATE_H

brainstorm.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

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

register.cpp

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

login.cpp

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

communicate.cpp

#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);//发送数据
}

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 

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

    return a.exec();
}

你可能感兴趣的:(linux,c++,qt,qt,开发语言,c++)