《头脑王者》——在线排位答题系统源码

声明:这些源码尚有可完善之处,因本人能力尚浅,如有错误,还望指正,谢谢!

服务器端
1. widget.h & widget.cpp

//widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>   // tcp服务器类
#include <QTcpSocket>   // 套接字类
#include "Mythread.h"
#include <QVector>
#include <QMap>
#include <QMutex>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    void timerEvent(QTimerEvent *event);

    QString SearchForName(QTcpSocket *client);

    // 由客户端传递过来的 对手的 昵称 ,找到对应的 socket
    QTcpSocket* SearchForSocket(QString name);
public slots:
    // 接受客户端连接
    void acceptClient();

    // 接受客户端成功登陆信息,保存到在线用户容器中
    void rec_user_info(QString name, QTcpSocket* client);

    // 接受客户端发送匹配申请的信息
    void rec_client_match_soket(QTcpSocket *client_Socket);

    // 接受客户端转发的分数,实现同步显示
    void rec_tran_score(QTcpSocket* client, int score, QString name);

    // 接收客户端发送的答题结果,更新数据库信息
    void rec_match_result(QTcpSocket* client, int score);

    // 接收客户端退出的请求信号
    void Remove_client(QTcpSocket* client);

private:
    Ui::Widget *ui;

    QTcpServer *_tcpServer;
    QTcpSocket *_tcpSocket;

    QVector<QTcpSocket *> PKSocket;     // 保存 匹配列表中的 客户端的 SOCKET
                                        // 匹配成功后,删除 这两个套接字

    QMap<QString, QTcpSocket*> Online;  // 在线用户信息

    Recv sendToClient1, sendToClient2;

    int timeID;
    QMutex mutex;

    QSqlDatabase db;            // 数据库句柄
};

#endif // WIDGET_H



//widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "Mythread.h"
#include <QTime>
#include <QTimerEvent>
#include <QMutexLocker>
#include <QMap>
#include <iterator>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 创建服务器
    _tcpServer = new QTcpServer(this);

    // 监听ip 地址和端口
    _tcpServer->listen(QHostAddress::Any, 9999);

    // 等待客户端连接
    connect (_tcpServer, SIGNAL(newConnection()), this, SLOT(acceptClient()));

    timeID = startTimer(1000);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::acceptClient()
{
    // 因为可能有多个一起连接,要处理所有的客户端
    while (_tcpServer->hasPendingConnections())
    {
        // 接受客户端连接
        QTcpSocket *client_pthread = _tcpServer->nextPendingConnection();
        Mythread *pthread = new Mythread(client_pthread, this);

        connect(pthread, SIGNAL(send_user_info(QString,QTcpSocket*)), this, SLOT(rec_user_info(QString,QTcpSocket*)));

        connect(pthread, SIGNAL(client_match_socket(QTcpSocket*)),this, SLOT(rec_client_match_soket(QTcpSocket*)), Qt::QueuedConnection);

        connect(pthread, SIGNAL(send_trans_score(QTcpSocket*,int,QString)), this, SLOT(rec_tran_score(QTcpSocket*,int,QString)));

        connect(pthread, SIGNAL(send_match_result(QTcpSocket*,int)), this, SLOT(rec_match_result(QTcpSocket*,int)));

        connect(pthread,SIGNAL(removeFromMap(QTcpSocket*)), this, SLOT(Remove_client(QTcpSocket*)));

        pthread->start();
    }

}

void Widget::rec_client_match_soket(QTcpSocket *client_Socket)
{
    qDebug() <<"已添加到排位列表";
    mutex.lock();
    PKSocket.push_back(client_Socket);
    mutex.unlock();
}

// 类似于工作在后台,一直监视数组中的人数
void Widget::timerEvent(QTimerEvent *event)
{
    // 每一秒监测数组里的 客户端套接字 是否可以进行PK
    // 将可以匹配的两个socket发送匹配信号后,将其清出数组
    // qDebug() << PKSocket.size();
    mutex.lock();
    if(PKSocket.size() >= 1)
    {
        /*
         *向第一个客户发送对手昵称
         */
        sendToClient1.cmd       = MATCHINGSUC;
        sendToClient1.EnemyName = Widget::SearchForName(PKSocket.at(0));

        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out<<(qint16)0;
        out<<sendToClient1;
        out.device()->seek(0);
        out<<(qint16)(block.size() - sizeof(qint16));


        /*
         *向第二个客户发送对手昵称
         */
//        sendToClient2.cmd   = MATCHINGSUC;
//        sendToClient2.EnemyName = Widget::SearchForName(PKSocket.at(0));

//        QByteArray block1;
//        QDataStream out1(&block1, QIODevice::WriteOnly);
//        out1<<(qint16)0;
//        out1<<sendToClient2;
//        out1.device()->seek(0);
//        out1<<(qint16)(block1.size() - sizeof(qint16));

        PKSocket.at(0)->write(block, block.length());
        //PKSocket.at(1)->write(block1, block1.length());

        PKSocket.pop_front();
        //PKSocket.pop_front();
    }
    mutex.unlock();
}

// 将 昵称 与 socket 绑定插入 map 中,便于查询
void Widget::rec_user_info(QString name, QTcpSocket* client)
{
    mutex.lock();
    Online.insert(name, client);
    mutex.unlock();
}

// 根据 socket 找到对应的 昵称
QString Widget::SearchForName(QTcpSocket *client)
{
    QMap<QString, QTcpSocket*>::iterator it = Online.begin();
    while(it != Online.end())
    {
        if(it.value() == client)
            return it.key();
        else
            it++;
    }
}

// 根据 昵称 找到对应的 socket
QTcpSocket* Widget::SearchForSocket(QString name)
{
    QMap<QString, QTcpSocket*>::iterator it = Online.begin();
    while(it != Online.end())
    {
        if(it.key() == name)
            return it.value();
        else
            it++;
    }
}

// 根据 昵称 找到对应的 socket,并将分数转发过去
void Widget::rec_tran_score(QTcpSocket* client, int score, QString name)
{
    QTcpSocket *tmp         = SearchForSocket(name);
    sendToClient1.cmd       = TRANSMIT;
    sendToClient1.EnemyName = SearchForName(client);
    sendToClient1.total     = score;

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out<<(qint16)0;
    out<<sendToClient1;
    out.device()->seek(0);
    out<<(qint16)(block.size() - sizeof(qint16));

    tmp->write(block, block.length());
}

// 更新数据库的信息
void Widget::rec_match_result(QTcpSocket* client, int score)
{
    QString name = SearchForName(client);
    QString tmpLevel;
    int tmpScore = 0;

    this->db = QSqlDatabase::addDatabase("QMYSQL");
    this->db.setHostName("localhost");
    this->db.setUserName("root");
    this->db.setPassword("123456");
    this->db.setDatabaseName("question_bank");

    bool ok = db.open();
    if(ok)
    {
        qDebug() <<"open database success";
    }
    else
    {
        qDebug() <<"open database error";
    }

//    qDebug() << "~~~~~~~~~~~~~~~~~~";
//    qDebug() << name;

    QSqlQuery query;

    // 先从数据库中获取之前的积分,将本次答题得分相加得到最新的积分
    query.prepare("SELECT *FROM user_info  WHERE name = ? ");
    query.addBindValue(name.toUtf8());
    query.exec();
    query.next();

    tmpScore = query.value(3).toInt() + score;
    qDebug() << tmpScore;

    switch(tmpScore / 100)
    {
        case 0:
            tmpLevel = QString("青铜");
            break;
        case 1:
            tmpLevel = QString("白银");
            break;
        case 2:
            tmpLevel = QString("黄金");
            break;
        case 3:
            tmpLevel = QString("铂金");
            break;
        case 4:
            tmpLevel = QString("钻石");
            break;
        case 5:
            tmpLevel = QString("星耀");
            break;
        case 6:
            tmpLevel = QString("王者");
            break;
         default:
            break;
    }

    // 更新得分和段位
    query.prepare("UPDATE user_info SET level = ?, total = ? WHERE name = ?");
    query.addBindValue(tmpLevel.toUtf8());
    query.addBindValue(tmpScore);
    query.addBindValue(name.toUtf8());
    query.exec();

    sendToClient1.cmd   = UPDATESCORE;
    sendToClient1.level = tmpLevel;
    sendToClient1.total = tmpScore;

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out<<(qint16)0;
    out<<sendToClient1;
    out.device()->seek(0);
    out<<(qint16)(block.size() - sizeof(qint16));
    client->write(block, block.length());

}

// 删除退出的客户端
void Widget::Remove_client(QTcpSocket *client)
{
    QMap<QString, QTcpSocket*>::iterator it = Online.begin();
    while(it != Online.end())
    {
        if(it.value() == client)
        {
            mutex.lock();
            Online.erase(it);       
            mutex.unlock();
        }
        else
            it++;
    }
}

2.mythread.h & mythread.cpp

// mythread.h 
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include <QTcpSocket>
#include <QTcpServer>
#include <QtSql>

#define  REGISTER           100001
#define  LOGIN              100002
#define  ALLOWLOGIN         100003
#define  REFUSELOGIN        100004
#define  MATCHING           100005
#define  MATCHINGSUC        100006
#define  GAMEING            100007
#define  FIRSTONGAMESTAT    100008
#define  OTHERONAGMESTAT    100009
#define  TRANSMIT           100010
#define  MATCHRESULT        100011
#define  UPDATESCORE        100012
#define  QUIT               100013

struct info
{
    int cmd;
    QString name;           // 昵称
    QString passwd;         // 密码
    QString tele;           // 手机号码
    int total;              // 总分
    QString level;          // 段位

    /*
     * 问题、选项、正确答案
     */
    QString ques;
    QString answer1;
    QString answer2;
    QString answer3;
    int correct;

    QString EnemyName;
};

typedef struct info Recv;

class Mythread : public QThread
{
    Q_OBJECT
public:
    explicit Mythread(QTcpSocket *client, QObject *parent = 0);
    void run();

    void user_Register();       // 用户注册申请
    void user_Login();          // 用户登录申请
    void user_match();          // 用户匹配申请
    void sendQues();            // 向用户分发题目
                                // 重载定时器函数
    void timerEvent(QTimerEvent *event);

signals:
    void client_match_socket(QTcpSocket *client);  // 将客户端的socket发给 主窗口保存

    void send_user_info(QString name, QTcpSocket* client);

    void send_trans_score(QTcpSocket* client, int score, QString name);

    void send_match_result(QTcpSocket* client, int score);

    void removeFromMap(QTcpSocket* client);

public slots:
    void dealClientData();

private:
    QTcpSocket *client;         // 客户端的socket
    QSqlDatabase db;            // 数据库句柄
    qint16 blocksize;
    Recv   Rec_msg;             // 接收的信息结构体

    QVector<QString> ques;      // 题干
    QVector<QString> answer1;   // 选项 1
    QVector<QString> answer2;   // 选项 2
    QVector<QString> answer3;   // 选项 3
    QVector<int> correct;       // 正确答案

    int timeID;                 // 定时器
    int num;
    int count;

};

inline QDataStream& operator << (QDataStream& out, Recv& send_msg)
{
    out << send_msg.cmd;
    out << send_msg.name;
    out << send_msg.passwd;
    out << send_msg.tele;
    out << send_msg.total;
    out << send_msg.level;

    out << send_msg.ques;
    out << send_msg.answer1;
    out << send_msg.answer2;
    out << send_msg.answer3;
    out << send_msg.correct;

    out << send_msg.EnemyName;
    return out;
}

inline QDataStream& operator >> (QDataStream& in, Recv& send_msg)
{
    in >> send_msg.cmd;
    in >> send_msg.name;
    in >> send_msg.passwd;
    in >> send_msg.tele;
    in >> send_msg.total;
    in >> send_msg.level;

    in >> send_msg.ques;
    in >> send_msg.answer1;
    in >> send_msg.answer2;
    in >> send_msg.answer3;
    in >> send_msg.correct;

    in >> send_msg.EnemyName;
    return in;
}

#endif // MYTHREAD_H

// mythread.cpp

#include "Mythread.h"
#include <QObject>
#include <QDebug>
#include <QTimerEvent>
#include <QTime>

Mythread::Mythread(QTcpSocket *client, QObject *parent) : QThread(parent)
{
    //connect(client, SIGNAL(readyRead()), this, SLOT(dealClientData()));
    this->client = client;
    num   = 0;
    count = -1;
}

void Mythread::run()
{
    connect(client, SIGNAL(readyRead()), this, SLOT(dealClientData()));
    qDebug() << "新的线程";
}

void Mythread::dealClientData()
{
    QDataStream in(client);
    blocksize = 0;
    if(blocksize == 0)
    {
        if(client->bytesAvailable() < (int)sizeof(qint16))
        {
            return;
        }
        in >> blocksize;
    }
    if(client->bytesAvailable() < blocksize)
    {
        return;
    }

    in >> Rec_msg;

    qDebug() << "11111" <<Rec_msg.cmd;
    qDebug() << "Enemy name" << Rec_msg.EnemyName;

    switch (Rec_msg.cmd) {
    case REGISTER:
        user_Register();
        break;
    case LOGIN:
        user_Login();
        break;
    case MATCHING:
        user_match();
        break;
    case GAMEING:
        sendQues();
        break;
    case TRANSMIT:
        emit send_trans_score(client, Rec_msg.total, Rec_msg.EnemyName);
        break;
    case MATCHRESULT:
        emit send_match_result(client, Rec_msg.total);
        break;
    case QUIT:
        emit removeFromMap(client);
        break;
    default:
        break;
    }
}

/*
 * 往数据库中添加注册信息
 */
void Mythread::user_Register()
{

    this->db = QSqlDatabase::addDatabase("QMYSQL");
    this->db.setHostName("localhost");
    this->db.setUserName("root");
    this->db.setPassword("123456");
    this->db.setDatabaseName("question_bank");

    bool ok = db.open();
    if(ok)
    {
        qDebug() <<"open database success";
    }
    else
    {
        qDebug() <<"open database error";
    }


    QSqlQuery query;

    Rec_msg.level  = "无段位";
    query.prepare("INSERT INTO user_info VALUES (?, ?, ?, ?, ?)");
    query.addBindValue(Rec_msg.name.toUtf8());
    query.addBindValue(Rec_msg.passwd);
    query.addBindValue(Rec_msg.tele);
    query.addBindValue(0);
    query.addBindValue(Rec_msg.level.toUtf8());

    query.exec();

    db.close();
}

/*
 * 验证登录信息
 */
void Mythread::user_Login()
{
    this->db = QSqlDatabase::addDatabase("QMYSQL");
    this->db.setHostName("localhost");
    this->db.setUserName("root");
    this->db.setPassword("123456");
    this->db.setDatabaseName("question_bank");

    bool ok = db.open();
    if(ok)
    {
        qDebug() <<"open database success";
    }
    else
    {
        qDebug() <<"open database error";
    }


    QSqlQuery query;
    qDebug() << Rec_msg.name << Rec_msg.passwd;

    query.prepare("select *from user_info");
    query.exec();

    bool Flag = false;

    while(query.next())
    {
        if(query.value(0).toString().toLocal8Bit() == Rec_msg.name
                && query.value(1).toString() == Rec_msg.passwd)
        {
            Flag = true;
            Rec_msg.level = query.value(4).toString().toLocal8Bit();
            Rec_msg.total = query.value(3).toInt();
            break;
        }
    }

    if(Flag)
    {
//        qDebug() <<"Flag == true";
        Rec_msg.cmd = ALLOWLOGIN;

        qDebug() << Rec_msg.level;
        qDebug() << Rec_msg.total;

        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out<<(qint16)0;
        out<<Rec_msg;
        out.device()->seek(0);
        out<<(qint16)(block.size() - sizeof(qint16));
        client->write(block, block.length());

        emit send_user_info(Rec_msg.name, client);
    }
    else
    {
        Rec_msg.cmd = REFUSELOGIN;
        qDebug() <<"Flag == false";
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out<<(qint16)0;
        out<<Rec_msg;
        out.device()->seek(0);
        out<<(qint16)(block.size() - sizeof(qint16));
        client->write(block, block.length());
    }

    db.close();

}

/*
 * 客户端申请匹配
 */
void Mythread::user_match()
{
   emit client_match_socket(client);
}

/*
 * 向用户分发题目
 * 一次性将所有题目抽取出来,放在容器内,利用定时器控制分发不同的题
 */
void Mythread::sendQues()
{
    int arr[20] = {0};
    int length = 20;

    for(int i = 0; i < 20; i++)
    {
        arr[i] = i + 1;
    }

    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    for (int i = 0; i < length - 1; i++)
    {
        int index = qrand()%(length-i);

        int tmp = arr[index];
        arr[index] = arr[length-i-1];
        arr[length-i-1] = tmp;
    }

    this->db = QSqlDatabase::addDatabase("QMYSQL");
    this->db.setHostName("localhost");
    this->db.setUserName("root");
    this->db.setPassword("123456");
    this->db.setDatabaseName("question_bank");

    bool ok = db.open();
    if(ok)
    {
        qDebug() <<"open database success";
    }
    else
    {
        qDebug() <<"open database error";
    }


    for(int i = 1; i < 6; i++)
    {
        QSqlQuery query;
        query.prepare("select *from question where ID = ?");
        query.addBindValue(arr[i]);
        query.exec();
        query.next();

        ques.push_back(QString(query.value(1).toString()));
        answer1.push_back(QString(query.value(2).toString()));
        answer2.push_back(QString(query.value(3).toString()));
        answer3.push_back(QString(query.value(4).toString()));
        correct.push_back(query.value(5).toInt());
    }

    db.close();

    timeID = startTimer(1000);
}

void Mythread::timerEvent(QTimerEvent *event)
{
    // 5秒 一题,第一次发题目时,触发客户端的计时器,让客户端进行答题界面
    // 第二次及以后的发题,不再触发客户端的计时器,仅仅是将题目添加到客户端的容器中
    if(num++ % 5 == 0 && count != 4)
    {
        ++count;

        if(count == 0)
            Rec_msg.cmd     = FIRSTONGAMESTAT;
        else
            Rec_msg.cmd     = OTHERONAGMESTAT;

        Rec_msg.ques    = ques.at(count).toLocal8Bit();
        Rec_msg.answer1 = answer1.at(count).toLocal8Bit();
        Rec_msg.answer2 = answer2.at(count).toLocal8Bit();
        Rec_msg.answer3 = answer3.at(count).toLocal8Bit();
        Rec_msg.correct = correct.at(count);

        qDebug() << Rec_msg.ques;
        qDebug() << Rec_msg.answer1;
        qDebug() << Rec_msg.answer2;
        qDebug() << Rec_msg.answer3;
        qDebug() << Rec_msg.correct;

        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out<<(qint16)0;
        out<<Rec_msg;
        out.device()->seek(0);
        out<<(qint16)(block.size() - sizeof(qint16));
        client->write(block, block.length());
    }

    if(count == 4)
    {
        killTimer(timeID);
        count = -1;
        num = 0;
        for(int i = 0; i < 5 ; i++)
        {
            ques.pop_back();
            answer1.pop_back();
            answer2.pop_back();
            answer3.pop_back();
            correct.pop_back();
        }
    }
}

3.main.cpp

#include "widget.h"
#include <QApplication>

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

    return a.exec();
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值