第五天项目日记
1、今日总结
今天完成客户端第一个界面的编写
客户端的注册、登录 发送账号密码的消息使用json格式
2、设计思路
主要操作数据库,以及一些消息的定义,由于客户端的socket再另一个线程里面,所以接收消息的工作都放在客户端的socket线程,ui的显示由线程发送信号给weidget
3、代码说明
3.1 服务器主要修改代码
dbhelper.h
新增登录和注册判断
dbhelper.cpp
QString dbhelper::logic_judge(QString name, QString pswd)
{
QSqlQuery query(*db);
query.prepare("select * from user where name = :name");
//用来绑定值
query.bindValue(":name",name);
bool res = query.exec();
if(!res){
qDebug()<<query.lastQuery();
}
while(query.next()){
QString _name = query.value("name").toString();
QString _pswd = query.value("pswd").toString();
if(_name == name){
if(pswd == _pswd){
return "success";
}else{
return "pswd_error";
}
}
}
if(!res){
//用来打印错误信息
qDebug()<<query.lastError().text();
}
return "no_name";
}
QString dbhelper::user_reg(QString name, QString pswd)
{
QSqlQuery query(*db);
//先看一下名字有没有
query.prepare("select * from user where name = :name");
query.bindValue(":name",name);
bool res = query.exec();
if(!res){
qDebug()<<query.lastError().text();
qDebug()<<query.lastQuery();
}
qDebug()<<"size = "<<query.size();
if(query.next()){
return "rename";//名字重复
}
query.prepare("insert into user (name,pswd) values (:name,:pswd)");
query.bindValue(":name",name);
query.bindValue(":pswd",pswd);
res = query.exec();
if(!res){
qDebug()<<query.lastQuery();
}
return "success";
}
message.h
修改msg的枚举
增加了函数接口
tcpsocket.h
void TcpSocket::msgPrase(QString msg_buf)
{
int item;
QString buf;
buf.clear();
item = msg_buf.section('|',0,0).trimmed().toInt();
int item_len = QString::number(item).length();
qDebug()<<"len"<<item_len;
buf = msg_buf.right(msg_buf.length()-item_len-1);
qDebug()<<"item"<<item<<" "<<buf;
//把它转换成消息发送给server
MyMessage message((MsgId)item,buf,buf.length());
if(message.isInEnum()){
if(message.getmsgid() == MSG_CLIENT_CLOSE || message.getmsgid() == MSG_CLITEN_CONNECT){
emit messageToServer(message); //发射消息信号
}else if(message.getmsgid() == MSG_LOGIC){
message.setmsgbuf(prase_MSG_LOGIC(message.getmsgbuf()));
this->write(message.toString().toLatin1(),message.toString().length());
}else if(message.getmsgid() == MSG_REGISTER){
message.setmsgbuf(prase_MSG_REG(message.getmsgbuf()));
this->write(message.toString().toLatin1(),message.toString().length());
}else if(message.getmsgid() == MSG_SHAKE_HAND){
message.setmsgbuf(" ");
this->write(message.toString().toLatin1(),message.toString().length());
}
}else{
qDebug()<<"无效数据";
}
}
QString TcpSocket::prase_MSG_LOGIC(QString message_buf)
{
QByteArray bytes = message_buf.toUtf8();
QJsonParseError jsonError;
QString name;
QString pswd;
QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError);
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误
if (doucment.isObject()){
QJsonObject object = doucment.object(); // 转化为对象
if (object.contains("Name")) {
QJsonValue value = object.value("Name");
if (value.isString()) {
name = value.toString();
qDebug() << "Name : " << name;
}
}else{
return "json_error_no_name";
}
if (object.contains("password")) {
QJsonValue value = object.value("password");
if (value.isString()) {
pswd = value.toString();
qDebug() << "pswd : " << pswd;
}
}else{
return "json_error_no_pswd";
}
}else{
return "json is no object";
}
}else{
return "json error";
}
return mydb->logic_judge(name,pswd);
}
QString TcpSocket::prase_MSG_REG(QString message_buf)
{
qDebug()<<message_buf;
QByteArray bytes = message_buf.toUtf8();
QJsonParseError jsonError;
QString name;
QString pswd;
QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError);
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误
if (doucment.isObject()){
QJsonObject object = doucment.object(); // 转化为对象
if (object.contains("Name")) {
QJsonValue value = object.value("Name");
if (value.isString()) {
name = value.toString();
qDebug() << "Name : " << name;
}
}else{
return "json_error_no_name";
}
if (object.contains("password")) {
QJsonValue value = object.value("password");
if (value.isString()) {
pswd = value.toString();
qDebug() << "pswd : " << pswd;
}
}else{
return "json_error_no_pswd";
}
}else{
return "json is no object";
}
}else{
return "json error";
}
return mydb->user_reg(name,pswd);
}
3.2客户端修改部分
连接的时候根据状态变化发送信号
登录的时候发送登录信号
注册的时候发送注册信号
直接转发收到的字符串
然后在主函数中判断
tcpthread.h
#ifndef TCPTHREAD_H
#define TCPTHREAD_H
#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QDebug>
#include "mymessage.h"
class TcpThread : public QThread
{
Q_OBJECT
public:
explicit TcpThread(QObject *parent = 0);
~TcpThread();
void startThread(const QString& ip, int port);
void stopThread();
void onSendTcp(QString);
void parse_msg(QString);
signals:
void send_tcpmsg(QString);
void send_logic_msg(QString);
void send_reg_msg(QString);
public:
int m_iSendData;
int m_iRecv_TimeOut;
int reconnect_num;
private:
QTcpSocket* m_TcpSocket;
bool m_isThreaStopped;
bool m_isOkConect;
QString m_QStrSocketIp;
int m_nSockPort;
QByteArray m_datagram;
protected:
virtual void run();
private slots:
void onConnect();
void onDisConnect();
void onReadMsg();
public slots:
};
#endif // TCPTHREAD_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QMessageBox>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include "tcpthread.h"
#include "mymessage.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void Logic_Init();//登录初始化
void User_Init();//使用界面初始化
void connectToServer(); //用连接服务器
public slots:
void connect_state(QString); //用来控制连接状态
void connect_success(); //用来判断连接状态
void logic_msg_recv(QString); //用来处理登录消息
void reg_msg_recv(QString); //用来处理注册消息
private slots:
void on_radioshow_clicked(bool checked); //用来显示密码和隐藏密码
void on_pushlogic_clicked();//用来登录
void on_pushquit_clicked();//用来退出
void on_pushregister_clicked();
signals:
void connect_enable(); //连接
private:
Ui::Widget *ui;
TcpThread *tcpThread;//用来启动socket线程
// bool status;
QString userName; //账号
QString passWord; //密码
int Logic_Flag; //用来判断是登录还是注册0注册1登录
};
#endif // WIDGET_H
tcpthread.cpp
#include "tcpthread.h"
TcpThread::TcpThread(QObject *parent) :
QThread(parent)
{
m_TcpSocket = nullptr ;
m_isThreaStopped = false;
m_isOkConect = false;
reconnect_num = 0;
}
TcpThread::~TcpThread()
{
m_isThreaStopped = true;
quit();
wait();
}
void TcpThread::startThread(const QString &ip, int port)
{
m_QStrSocketIp = ip;
m_nSockPort = port;
m_isThreaStopped = false;
start();
}
void TcpThread::stopThread()
{
reconnect_num = 0;
m_isThreaStopped = true;
}
void TcpThread::run()
{
bool b_recv_flag = false;
emit send_tcpmsg("connecting");
if (!m_TcpSocket)
{
m_TcpSocket = new QTcpSocket(this);
connect(m_TcpSocket, SIGNAL(readyRead()), this, SLOT(onReadMsg()),Qt::DirectConnection);//让接受函数在run子线程中执行(发送者执行)
connect(m_TcpSocket, SIGNAL(connected()), this, SLOT(onConnect()));
connect(m_TcpSocket, SIGNAL(disconnected()), this, SLOT(onDisConnect()));
}
while (!m_isThreaStopped)
{
//检测客户端 socket指针是否为空
if(b_recv_flag)
msleep(100);
if (!m_isOkConect)
{
// 终止当前连接并重置套接字(立即关闭套接字,丢弃写缓冲区中的任何挂起数据)
m_TcpSocket->abort();
m_TcpSocket->connectToHost(m_QStrSocketIp, m_nSockPort);
//等待连接。。。延时三秒,三秒内连不上返回false
m_isOkConect = m_TcpSocket->waitForConnected(3000);
m_iRecv_TimeOut = -1;
reconnect_num++;
}
if (!m_isOkConect)
{
msleep(1);
if(reconnect_num <3){
QString reconnect = "reconnect_"+QString::number(reconnect_num);
emit send_tcpmsg(reconnect);
continue;
}else{
emit send_tcpmsg("overtime");
break;
}
}
/*else
{
qDebug()<<"tcp_flag:"<<m_iRecv_TimeOut;
QString msg_buf = " ";
MyMessage msg_shake_hand(MSG_SHAKE_HAND,msg_buf,msg_buf.length());
// onSendTcp(msg_shake_hand.toString());
onSendTcp(msg_buf);
}*/
b_recv_flag = m_TcpSocket->waitForReadyRead(100);
if (b_recv_flag)
{
m_iRecv_TimeOut = 0;
}
else
{
m_iRecv_TimeOut++;
if(m_iRecv_TimeOut>150) m_iRecv_TimeOut=150;
}
if(m_iRecv_TimeOut >= 100){
QString msg_buf = " ";
MyMessage msg_shake_hand(MSG_SHAKE_HAND,msg_buf,msg_buf.length());
onSendTcp(msg_shake_hand.toString());
// onSendTcp(msg_buf);
}
if(m_iRecv_TimeOut >= 150)
{
m_iRecv_TimeOut = -1;
}
}
m_TcpSocket->disconnectFromHost();
}
void TcpThread::onConnect()
{
reconnect_num = 0;
emit send_tcpmsg("connect");
}
void TcpThread::onDisConnect()
{
//socket一旦断开则自动进入这个槽函数
//通过把 m_isOkConect 设为false,在socket线程的run函数中将会重新连接主机
qDebug()<<"socket is disconnect!"<<endl;
m_isOkConect = false;
m_iRecv_TimeOut = -1;
emit send_tcpmsg("disconnect");
}
void TcpThread::onReadMsg()
{
if(m_TcpSocket->bytesAvailable() <= 0)
{
// 判定连接失败
m_TcpSocket->disconnectFromHost();
}
while (m_TcpSocket->bytesAvailable() > 0)
{
// 接收数据
m_datagram.clear();
m_datagram.resize(m_TcpSocket->bytesAvailable());
m_TcpSocket->read(m_datagram.data(), m_datagram.size());
QString str_tcp_receive = QString::fromLocal8Bit(m_datagram);
parse_msg(str_tcp_receive);
qDebug()<<str_tcp_receive;
msleep(100);
}
}
void TcpThread::onSendTcp(QString str_info)
{
if((!m_TcpSocket)||m_TcpSocket->state()!=QAbstractSocket::ConnectedState)
return;
m_iSendData = m_TcpSocket->write(str_info.toLatin1(), str_info.length());
m_TcpSocket->flush();
if (m_iSendData < 0)
{
m_TcpSocket->disconnectFromHost();
return;
}
msleep(1000);
}
void TcpThread::parse_msg(QString msg_buf) //解析接收到的消息
{
int item;
QString buf;
item = msg_buf.section('|',0,0).trimmed().toInt();
int item_len = QString::number(item).length();
buf = msg_buf.right(msg_buf.length()-item_len-1);
if(item == (int)MSG_LOGIC){
emit send_logic_msg(buf);//发送登录消息
return;
}else if(item == (int)MSG_REGISTER){
emit send_reg_msg(buf);//发送注册消息
return;
}
return;
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QThread>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
tcpThread = nullptr;
// status = false;
Logic_Init();
QObject::connect(this,SIGNAL(connect_enable()),this,SLOT(connect_success()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::Logic_Init()
{
this->setWindowTitle("登录/注册");
ui->linepswd->show();
ui->lineUser->show();
ui->radioshow->show();
ui->pushlogic->show();
ui->pushregister->show();
ui->linepswd->setEchoMode(QLineEdit::Password);
ui->pushAdd->hide();
ui->pushCreate->hide();
ui->listView->hide();
ui->pushquit->hide();
}
void Widget::User_Init()
{
this->setWindowTitle("云木直播平台");
ui->linepswd->hide();
ui->lineUser->hide();
ui->radioshow->hide();
ui->pushlogic->hide();
ui->pushregister->hide();
ui->label->hide();
ui->label_2->hide();
ui->label_3->hide();
ui->pushAdd->show();
ui->pushCreate->show();
ui->listView->show();
ui->pushquit->show();
}
void Widget::connectToServer()
{
QString Address_Ip = "127.0.0.1";
int port = 8010;
if(!tcpThread){
tcpThread = new TcpThread;
QObject::connect(tcpThread,SIGNAL(send_tcpmsg(QString)),this,SLOT(connect_state(QString)));
QObject::connect(tcpThread,SIGNAL(send_logic_msg(QString)),this,SLOT(logic_msg_recv(QString)));
QObject::connect(tcpThread,SIGNAL(send_reg_msg(QString)),this,SLOT(reg_msg_recv(QString)));
}
tcpThread->startThread(Address_Ip,port);
}
void Widget::connect_state(QString message)
{
qDebug()<<message;
if(message == "connecting") {
ui->label_connect_state->setText("连接中...");
}else if(message == "connect"){
ui->label_connect_state->clear();
emit connect_enable();
return;
}else if(message == "disconnect"){
tcpThread->stopThread();
return;
}else if(message == "overtime"){
QMessageBox::warning(NULL,tr("无法连接"),tr("无法连接请稍后再试"));
ui->label_connect_state->clear();
tcpThread->stopThread();
return;
}else if(message.left(9) == "reconnect"){
QString text = "第"+message.right(1)+"次重连中";
ui->label_connect_state->setText(text);
}
}
void Widget::connect_success()
{
QJsonObject json;
json.insert("Name",userName);
json.insert("password",passWord);
QJsonDocument document;
document.setObject(json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
MyMessage mymsg((Logic_Flag?MSG_LOGIC:MSG_REGISTER),strJson,strJson.length());
qDebug()<<mymsg.toString();
tcpThread->onSendTcp(mymsg.toString());
}
void Widget::logic_msg_recv(QString message)
{
if(message == "success"){
QMessageBox::about(NULL,"登录","登录成功");
User_Init();
return;
}else if(message == "pswd_error"){
QMessageBox::warning(NULL," 密码错误","密码输入错误请重试");
tcpThread->stopThread();
return;
}
else if(message == "no_name"){
QMessageBox::warning(NULL,"未注册","请先注册账号");
tcpThread->stopThread();
return;
}
}
void Widget::reg_msg_recv(QString message)
{
if(message == "success"){
QMessageBox::about(NULL,"注册","注册成功");
tcpThread->stopThread();
return;
}else if(message == "rename"){
QMessageBox::warning(NULL,"用户名重复","请输入其他用户名");
tcpThread->stopThread();
}else{
tcpThread->stopThread();
}
}
void Widget::on_radioshow_clicked(bool checked)
{
if(checked){
ui->linepswd->setEchoMode(QLineEdit::Normal);
}else{
ui->linepswd->setEchoMode(QLineEdit::Password);
}
}
void Widget::on_pushlogic_clicked()
{
if(ui->lineUser->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("账号不能为空"));
return;
}
if(ui->linepswd->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("密码不能为空"));
return;
}
userName = ui->lineUser->text();
passWord = ui->linepswd->text();
Logic_Flag = 1;
//连接
connectToServer();
}
void Widget::on_pushquit_clicked()
{
tcpThread->stopThread();
QMessageBox::about(NULL,tr("退出"),tr("点击退出"));
this->close();
}
void Widget::on_pushregister_clicked()
{
if(ui->lineUser->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("账号不能为空"));
return;
}
if(ui->linepswd->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("密码不能为空"));
return;
}
userName = ui->lineUser->text();
passWord = ui->linepswd->text();
Logic_Flag = 0;
//连接
connectToServer();
}
4、项目文件
5、效果展示
数据库中的用户如下
1、测试已有用户是否可以注册
2、测试未注册用户是否可以登录
3、测试未注册用户能否注册
4、测试已有用户密码以及能否登录成功
这里服务器用户列表会显示是因为只要建立了连接就会显示,后面会改掉
6、小结
使用了一些QJson
坚持就是胜利!!!!!!