关于网盘设计,在客户端登录注册注销部分,主要有以下五个部分:消息类型、界面设计、注册、登录、注销
消息类型:我们可以把它写成枚举类型的
界面设计:
注册:用户名唯一,防止重复注册。查询数据是否存在用户注册的用户名,存在则回复失败,不存在的回复成功。在服务器新建文件夹用作该用户的网盘区域
登录:防止重复登陆,在数据库中添加字段用于记录用户是否在线,在线则不允许登录
注销:删除好友信息,删除个人信息,删除网盘文件。
目录
1、消息类型
2、界面设计
2.1、拖入对应的控件,并进行命名
2.2、登录、注册、注销都进行转到槽、
3、注册
3.1、编写注册代码
3.2、通过socket将他发送出去
3.3、在服务器端收数据
3.4、测试
A、运行TcpServer
B、运行TcpClient
C、在TcpServer可以看到该用户的信息以及数据类型
3.5、将数据写到数据库里面去
A、在Operator.h文件里面处理注册
B、判断消息类型
C、在协议protocol.h里面添加宏
3.6、运行测试
A、运行TcpServer
B、运行TcpClient
C、再注册一次tony,就会提示
D、查看cloud.db数据库里面是否有数据
4、登录
4.1、点击登录按钮,右键转到槽
A、定义枚举类型(用户端和服务器端都要添加,协议必须是一致的)
B、槽代码
C、类似于3.5节,我们在recv_msg里面添加 case ENUM_MSG_TYPE_LOGIN_REQUEST
D、同样,在协议.h文件里面写上
E、添加定义
F、添加宏定义——在协议.h里面
G、添加case ENUM_MSG_TYPE_LOGIN_REQUEST
H、在客户端tcpclient.cpp的recv_Msg里面添加
4.2、运行测试
A、运行TcpServer
B、运行TcpClient
C、再登录一次jack,就会提示
D、查看cloud.db数据库里面数据是否发生变化,我们发现jack的online字段已经改为1
5、注销
5.1、在登陆的时候,在服务器的mytcpsocket.h里添加一个成员函数
5.2、当登录成功的时候,就将他的名字给记录下来
5.3、创建一个槽函数,用来处理客户端下线
5.4、关联信号槽
5.5、在数据库.h里面定义一个函数
5.6、写一个信号,标志下线
5.7、在server定义槽函数,接收数据
5.8、删除socket槽函数
5.9、关联槽函数
6、测试
6.1、查看当前数据库信息
6.2、打开三个client用户的终端
A、输入三个用户名和密码都点击登录
B、查看终端显示
C、关闭jack,jack的online字段置为0
D、关闭lucy,lucy的online字段置为0
E、关闭tony,tony的online字段置为0
7、最终代码
1、消息类型
我们设计成枚举类型的
//消息类型
enum ENUM_MSG_TYPE
{
//最小的
ENUM_MSG_TYPE_MIN = 0,
//登录请求
ENUM_MSG_TYPE_REGIST_REQUEST,
//登录回复
ENUM_MSG_TYPE_REGIST_RESPOND,
//设置为32位。因为我们是要赋给一个无符号的整型
ENUM_MSG_TYPE_MAX = 0x00ffffff,
};
在服务器端也得写
2、界面设计
2.1、拖入对应的控件,并进行命名
2.2、登录、注册、注销都进行转到槽、
void TcpClient::on_login_pb_clicked()
{
}
void TcpClient::on_regist_pb_clicked()
{
}
void TcpClient::on_cancel_pb_clicked()
{
}
3、注册
3.1、编写注册代码
void TcpClient::on_regist_pb_clicked()
{
//获得界面数据
QString strName = ui->name_le->text();
QString strPwd = ui->pwd_le->text();
//如果是空的
if(!strName.isEmpty()&&!strPwd.isEmpty()){
//发送注册请求
//将用户名和密码填充到pdu里面去
//消息部分不用放数据,直接写0
PDU *pdu = mkPDU(0);
//消息类型
pdu->uiMsgType = ENUM_MSG_TYPE_REGIST_REQUEST;
//将数据拷贝进去,也可以用strnpy
memcpy(pdu->caData, strName.toStdString().c_str(),32);
//从后面32个字节开始放pdu->caData+32,放我的密码,也都是放32个
memcpy(pdu->caData+32, strPwd.toStdString().c_str(),32);
//通过socket将他发送出去
m_tcpSocket.write((char*)pdu, pdu->uiPDULen);
//释放空间
free (pdu);
pdu =NULL;
}else {
QMessageBox::critical(this, "信息注册", "注册失败,用户名或者密码为空");
}
}
3.2、通过socket将他发送出去
//通过socket将他发送出去
m_tcpSocket.write((char*)pdu, pdu->uiPDULen);
//释放空间
free (pdu);
pdu =NULL;
3.3、在服务器端收数据
//打印数据
char caName[32] = {'\0'};
char caPwd[32] ={'\0'};
strncpy(caName, pdu->caData, 32);
strncpy(caPwd, pdu->caData+32, 32);
qDebug()<<caName<<caPwd<<pdu->uiMsgType;
3.4、测试
A、运行TcpServer
B、运行TcpClient
输入以下信息
C、在TcpServer可以看到该用户的信息以及数据类型
3.5、将数据写到数据库里面去
A、在Operator.h文件里面处理注册
因为是注册,不需要修改传过来的数据类型,所以直接写成const类型
bool handleRegist(const char *name, const char *Pwd);
bool OperateDB::handleRegist(const char *name, const char *Pwd)
{
//将要执行的语句放在caQuery里面
// char caQuery[128] = {'\0'};
// sprintf(caQuery, "");
if(NULL==name || NULL== Pwd){
//考虑形参的有效性
qDebug()<<"name |pwd is NUll";
return false;
}
//拼接
QString data = QString("insert into usrInfo (name,pwd) values(\'%1\',\'%2\')").arg(name).arg(Pwd);
qDebug()<<data;
QSqlQuery query;
return query.exec(data);
}
B、判断消息类型
在TcpClient.h和.cpp文件里面添加对应内容
void TcpClient::recvMsg()
{
//打印当前可读的数据大小
qDebug()<<m_tcpSocket.bytesAvailable();
//收数据
uint uiPDULen =0;
//先收4个字节大小的数
m_tcpSocket.read((char*)&uiPDULen, sizeof (uint));
//根据总的大小,计算实际消息长度
uint uiMsgLen = uiPDULen-sizeof (PDU);
//根据实际消息长度,产生一个pdu,接收剩余的数据
PDU *pdu = mkPDU(uiMsgLen);
//(char*)pdu +sizeof (uint)从这儿开始放,放uiPDULen-sizeof (uint)些数据
m_tcpSocket.read((char*)pdu +sizeof (uint),uiPDULen-sizeof (uint));
//qDebug()<<pdu->uiMsgType<<(char*)pdu->caMsg;
//判断消息类型
switch (pdu->uiMsgType) {
case ENUM_MSG_TYPE_REGIST_RESPOND:
{
if(0== strcmp(pdu->caData, REGIST_OK)){
QMessageBox::information(this, "注册",REGIST_OK);
}
else if(0== strcmp(pdu->caData, REGIST_FAILED)){
QMessageBox::warning(this, "注册", REGIST_FAILED);
}
break;
}
default:
break;
}
free (pdu);
pdu =NULL;
}
C、在协议protocol.h里面添加宏
#define REGIST_OK "regist ok"
#define REGIST_FAILED "regist failed : name existed"
3.6、运行测试
A、运行TcpServer
B、运行TcpClient
输入以下信息
C、再注册一次tony,就会提示
D、查看cloud.db数据库里面是否有数据
注册成功!!!!!!!
4、登录
在https://blog.csdn.net/wjl990316fddwjl/article/details/136983744这一节,我们创建用户表的时候,使用了online字段,这个字段假如是1,则登录过了,就不可以在登陆了,假如是0那就可以继续登录。
在之前我们已经添加的数据里面,可以看到,现在的在线情况都是0.
4.1、点击登录按钮,右键转到槽
A、定义枚举类型(用户端和服务器端都要添加,协议必须是一致的)
enum ENUM_MSG_TYPE
{
//最小的
ENUM_MSG_TYPE_MIN = 0,
//注册
ENUM_MSG_TYPE_REGIST_REQUEST, //注册请求
ENUM_MSG_TYPE_REGIST_RESPOND, //注册回复
//登录
ENUM_MSG_TYPE_LOGIN_REQUEST, //登录请求
ENUM_MSG_TYPE_LOGIN_RESPOND, //登录回复
}
B、槽代码
void TcpClient::on_login_pb_clicked()
{
//获得界面数据
QString strName = ui->name_le->text();
QString strPwd = ui->pwd_le->text();
//如果是空的
if(!strName.isEmpty()&&!strPwd.isEmpty()){
//发送登录请求
//将用户名和密码填充到pdu里面去
//消息部分不用放数据,直接写0
PDU *pdu = mkPDU(0);
//消息类型
pdu->uiMsgType = ENUM_MSG_TYPE_LOGIN_REQUEST;
//将数据拷贝进去,也可以用strnpy
memcpy(pdu->caData, strName.toStdString().c_str(),32);
//从后面32个字节开始放pdu->caData+32,放我的密码,也都是放32个
memcpy(pdu->caData+32, strPwd.toStdString().c_str(),32);
//通过socket将他发送出去
m_tcpSocket.write((char*)pdu, pdu->uiPDULen);
//释放空间
free (pdu);
pdu =NULL;
}else {
QMessageBox::critical(this, "登录", "登录失败,用户名或者密码为空");
}
}
C、类似于3.5节,我们在recv_msg里面添加 case ENUM_MSG_TYPE_LOGIN_REQUEST
case ENUM_MSG_TYPE_LOGIN_REQUEST:
{
//1、查看有没有这个用户,
//2、再就是查看有没有登陆过
break;
}
D、同样,在协议.h文件里面写上
//-------------------------登录-----------------------------
bool handleLogin(const char *name, const char *Pwd);
E、添加定义
bool OperateDB::handleLogin(const char *name, const char *Pwd)
{
//将要执行的语句放在caQuery里面
// char caQuery[128] = {'\0'};
// sprintf(caQuery, "");
if(NULL==name || NULL== Pwd){
//考虑形参的有效性
qDebug()<<"name |pwd is NUll";
return false;
}
//拼接一个查询语句
//必须三个条件都满足
QString data = QString("select * from usrInfo where name=\'%1\' and pwd=\'%2\' and online =0").arg(name).arg(Pwd);
qDebug()<<data;
QSqlQuery query;
//select * 返回的是一个结果集
query.exec(data);
//获得每一个结果集
//调用nect的时候会调用它的第一条数据,以此类推。直到没有数据,返回的是一个false
//条件不匹配那就是没有数据,直接false,退出函数
if(query.next()){
//更新online
data = QString("update usrInfo set online=1 where name=\'%1\' and pwd=\'%2\'").arg(name).arg(Pwd);
qDebug()<<data;
QSqlQuery query;
query.exec(data);
return true;
}else {
return false;
}
}
F、添加宏定义——在协议.h里面
#define LOGIN_OK "login ok!"
#define LOGIN_FAILED "login failed : name or pwd or relogin!"
G、添加case ENUM_MSG_TYPE_LOGIN_REQUEST
//------------------登录------------------
case ENUM_MSG_TYPE_LOGIN_REQUEST:
{
//1、查看有没有这个用户,
//2、再就是查看有没有登陆过
char caName[32] = {'\0'};
char caPwd[32] ={'\0'};
strncpy(caName, pdu->caData, 32);
strncpy(caPwd, pdu->caData+32, 32);
bool ret =OperateDB::getInstance().handleLogin(caName,caPwd);
PDU *respdu = mkPDU(0);
respdu->uiMsgType = ENUM_MSG_TYPE_LOGIN_RESPOND;
if(ret){
strcpy(respdu->caData, LOGIN_OK);
}else {
strcpy(respdu->caData, LOGIN_FAILED);
}
//发送
write((char*)respdu, respdu->uiPDULen);
//释放空间
free (respdu);
respdu =NULL;
break;
}
default:
break;
}
free (pdu);
pdu =NULL;
//qDebug()<<caName<<caPwd<<pdu->uiMsgType;
}
H、在客户端tcpclient.cpp的recv_Msg里面添加
//-----------------------------登录------------------------------
case ENUM_MSG_TYPE_LOGIN_RESPOND:
{
if(0== strcmp(pdu->caData, LOGIN_OK)){
QMessageBox::information(this, "登录",LOGIN_OK);
}
else if(0== strcmp(pdu->caData, LOGIN_FAILED)){
QMessageBox::warning(this, "登录", LOGIN_FAILED);
}
break;
}
4.2、运行测试
A、运行TcpServer
B、运行TcpClient
输入以下信息
C、再登录一次jack,就会提示
D、查看cloud.db数据库里面数据是否发生变化,我们发现jack的online字段已经改为1
登录完成!!!!!!!
5、注销
在登录的时候,我们将online字段设置成了1,那么我们在退出的时候,就应该将这个字段设置为0.要不然下次在登陆的时候还是1,就不可以再登录了。
在服务器那边,把产生的socket保存到了一个链表里面,那么再退出登陆的时候,我们就要将socket删除掉,因为客户端已经下线了,就没有必要了,如果不进行删除的话,下次再重新登陆过来就会产生新的socket,那这样就会类加成很多socket,就会占用很多资源,后面回产生资源耗尽,服务器崩溃。
5.1、在登陆的时候,在服务器的mytcpsocket.h里添加一个成员函数
到时候修改数据库状态的时候,可以通过修改它的名字来进行查找。
private:
QString m_strName;
5.2、当登录成功的时候,就将他的名字给记录下来
m_strName = caName;
5.3、创建一个槽函数,用来处理客户端下线
void clientOffline();
void MyTcpSocket::clientOffline()
{
//将在线状态设置为非在线状态
//将数据库的状态做修改
OperateDB::getInstance().handleOffline(m_strName.toStdString().c_str());
//删除链表的socket
emit offline(this);
}
5.4、关联信号槽
connect(this, SIGNAL(disconnected()), this, SLOT(clientOffline()));
5.5、在数据库.h里面定义一个函数
//-------------------------注销-----------------------------
void handleOffline(const char *name);
void OperateDB::handleOffline(const char *name)
{
if(NULL==name){
//考虑形参的有效性
qDebug()<<"name is NUll";
return;
}
QString data = QString("update usrInfo set online=0 where name=\'%1\'").arg(name);
qDebug()<<data;
QSqlQuery query;
query.exec(data);
}
5.6、写一个信号,标志下线
signals:
//下线_放地址
void offline(MyTcpSocket *mysocket);
5.7、在server定义槽函数,接收数据
public slots:
void deleteSocket(MyTcpServer *socket);
5.8、删除socket槽函数
public slots:
void deleteSocket(MyTcpSocket *mysocket);
void MyTcpServer::deleteSocket(MyTcpSocket *mysocket)
{
QList<MyTcpSocket*>::iterator iter = m_tcpSocketList.begin();
for(;iter!=m_tcpSocketList.end(); iter++)
{
if(mysocket==*iter){
delete *iter;
*iter = NULL;
m_tcpSocketList.erase(iter);
break;
}
}
for(int i=0; i<m_tcpSocketList.size();i++)
{
qDebug()<<m_tcpSocketList.at(i)->getName();
}
}
5.9、关联槽函数
connect(pTcpSocket, SIGNAL(offline(MyTcpSocket*)), this, SLOT(deleteSocket(MyTcpSocket*)));
6、测试
6.1、查看当前数据库信息
6.2、打开三个client用户的终端
A、输入三个用户名和密码都点击登录
B、查看终端显示
查看数据库发现这三个人的onlin字段都是1;
C、关闭jack,jack的online字段置为0
D、关闭lucy,lucy的online字段置为0
E、关闭tony,tony的online字段置为0
7、最终代码
http://链接:https://pan.baidu.com/s/1kwnan3pWmdIVgJjk4owCHw 提取码:fann