声明:这些源码尚有可完善之处,因本人能力尚浅,如有错误,还望指正,谢谢!
服务器端
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();
}