《头脑王者》--在线排位答题系统构思

《头脑王者》是一款在线排位制答题小应用,我们希望在学习完 QT 后,能做一个类似的应用,那么在真正开始着手之前,将功能细化、拆分,待整体思路清晰后,再开始写代码。

抛开 排位制 和 在线 两个修饰语,答题这个功能,我们还是比较熟悉的。

如何答题?

在学完 C++ 的容器后,对数据的存储有了更加方便的选择,在 QT 中 进行“单机版”模拟时,我们先使用链表这个容器保存题目、选项、正确答案、实际选择答案

QList <string> Quest;     // 题干
QList <string> Answer1;   // 选项 1
QList <string> Answer2;   // 选项 2
QList <string> Answer3;   // 选项 3
QList <string> CorAnsw;   // 正确选项
QList <string> Choice;    // 实际选项

此时,想要完成答题还需要一个定时器,通过这个定时器去触发一个事件,这个事件去完成实际选项的获取,题目和选项的切换(切换的方法有很多,可以利用 textEdit 的属性,也可以使用 stacked Widget 进行页面切换),得分的计算。在答题完毕时,通过消息对话框打印最终得分情况,那么这样“单机版”的答题也就完成了。

下一步,就是构建一个题库,每次从题库中抽取不同的题目,在构建这个题库时,我个人的想法是:表 包含 题干、选项、正确答案。
那么,实际答案的正确性如何判断因人而异,我想的是,在数据库中,就预先保存了正确答案是第几个选项(比如,有 A、B、C 三个选项,我将正确答案选项 2 保存在数据库中,这样 我通过控件的编号,就可以知道 你这题是否答对了)。

在创建题库时,可以使用 avicat Premium 进行数据库 和 表的建立,插入数据也很方便,但在QT中读出中文时,会出现一些乱码,暂时没能想到好的解决方法,只是使用了 .toLocal8bit() 应急,在随机抽题时,方法比较多就不多介绍了。

数据库建立好,大致就是下面这个样子;
这里写图片描述
单机版 + 数据库 实现后,我们来细化 服务器 和 客户端,将功能抽离,首先在 Client端,我们先建立好主界面、登录界面、注册界面,以及一些小细节的处理。。。

实现一个简单的主界面,有注册与登录两个功能按钮,两个按钮的槽函数分别实现注册与登录信息的获取及传递,这里顺便说一下,界面的跳转,根据个人喜好,可以在需要的时候, new 一个新的,并且把不需要的界面给 close 掉,当需要返回上一级时在 new 一个;这个方法我没使用,我使用的是一种比较 low 的方法,首先建立好所需的窗口对象,界面跳转时使用 hide 和 show
我做出的样子是这样子的。。。。(美工我就不管咯)

主界面
这里写图片描述

登录
这里写图片描述

注册
这里写图片描述

这样,简单的注册与登录界面就完成了,下面,我们要尝试搭建服务器,并且完成用户的登录及注册;

搭建服务器的几个步骤,这里也说一下:

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

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

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

与之对应的客户端也需要进行相关配置的设置:

    // 服务器的 IP 地址
    _IP = "192.168.1.127";

    // 服务器的 端口号
    _Port = 9999;

    _tcpSocket = new QTcpSocket(this);
    connect(_tcpSocket, SIGNAL(connected()), this, SLOT(connectServer()));

当客户端点击了注册或者登陆按钮时,将注册的信息都发给服务器,服务器接收到后,插入数据库中;发送时,使用了 QDataStream 进行发送,将流的前两个字节保存其有用数据的长度,读的时候,也是这样,先读取前两个字节的长度,在读后续内容;

发送
    // infomation 为保存信息的结构体
    infomation.cmd        = REGISTER;
    infomation.name       = name;
    infomation.passwd     = passwd;
    infomation.con_passwd = con_passwd;
    infomation.tele       = tele;

    // block 为QByteArray 的对象,out 为 QDataStream 的对象,通过输出流对象 out 保存数据并发送
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out<<(qint16)0;
    out<<infomation;
    out.device()->seek(0);
    out<<(qint16)(block.size() - sizeof(qint16));
    _tcpSocket->write(block, block.length());
接受
    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;

在搭建好服务器和客户端后,所需要完成的,就是注册与登录,这两个功能实现起来比较简单,无非在一些细节上需要考虑,例如说,注册填写的密码是否正确、昵称是否可用,这些在实现起来的时候,可以自行选择,这两部分如何实现,我在这就不多费口舌了,和之前写的聊天室基本相同,值得注意的是线程在什么时刻开启,以及相关的信号与槽如何设计,留给读者自行思考,我会在下一篇博客公布源码,还是希望你能自己思考。着重来讲一下,登录成功之后的事情吧;

首先,我做了一个这样的界面
这里写图片描述,在登陆成功之后,我希望向用户展示 Ta 的昵称、段位和积分,这个实现起来也不难,在登录成功后,服务器将你所需要的信息发送给你就 OK 了。

当你点击开始匹配的时候,会向服务器发送一个请求,请求服务器将你放入匹配列表中,当匹配列表中出现符合与你对战的情况时,匹配成功,服务器会发送匹配成功的标志告诉你,这样你就可以做答题前的准备了(界面的切换,相关控件的初始化等等);

那么,服务器是如何做到两个客户端之前同步答题的呢?
(这个系统,我没有对匹配进行段位限制,通俗的说,所有用户是随机组队的,比如说,你是黄金的,你也有可能遇上星耀。。。)

其实,当服务器一开始的时候,就会存在一个匹配列表(我使用的是一个 Vector 容器)用来存放申请匹配的用户的信息,存入的顺序完全就是客户端申请的顺序。服务器会有一个定时器,每秒去监测一些容器当前保存的用户数量,一旦 >= 2 的情况出现时,将最前面的两个进行组队,并且将这两个客户端从容器里踢出去。那么,对这两个匹配成功的客户端发送匹配成功的信息后,会立即从题库中抽取题目,分别在各自的线程中,将题目一题题的发送过去,这样就是一个简单的发题过程。

既然有发题,就会有接收题目的过程,并且在接收题目的时候,会有一些细节需要处理。什么细节?在第一次发送题目的时候,客户端需要做的事情有如下两件:第一、将接收到的题目先暂存在自己的容器中(读者会问,为什么不直接打印在答题界面呢?稍后为您揭晓);第二、触发答题定时器、用来切换题目、选项和计时所用。那么,之后的接收题目,只需要将题目暂存容器里就好了,切换题目的工作完全交给客户端自己去完成。这样的话,即便你服务器和客户端之间信息传递存在一定的时间差,也不会因为这段时间差而影响用户的正常答题、更直白的说,客户端的计时器是真正控制用户答题的、而服务器的计时器只是控制向客户端发送题目的时间间隔,因此我没有采用服务器的定时器去控制客户端的答题。

先看一下,我做的答题界面,
这里写图片描述
在这个界面,需要展示答题剩余时间、双方的得分情况、题干、选项;其中,倒计时、“你的得分”、题干、三个选项都是客户端依赖于自己的定时器去解决的、唯独“Ta的得分”,是要靠服务器发送过来的;

这里便涉及到了答题过程中会触发到的信号与槽了,在每个客户端点击某一选项时,都会去判断正确与否,一旦正确了,会改变自己分数,还会将这个分数发送给服务器,再让服务器发给“对手”,当“对手”收到后,刷新一下就好啦!当所以题目答题完毕后,系统会根据你的累计积分,对数据库中的信息进行更新,同样对这个显示界面也会刷新,那么这样,整个答题系统差不多就算完成了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值