网盘——客户端登陆注册注销请求

关于网盘设计,在客户端登录注册注销部分,主要有以下五个部分:消息类型、界面设计、注册、登录、注销

消息类型:我们可以把它写成枚举类型的

界面设计:

注册:用户名唯一,防止重复注册。查询数据是否存在用户注册的用户名,存在则回复失败,不存在的回复成功。在服务器新建文件夹用作该用户的网盘区域

登录:防止重复登陆,在数据库中添加字段用于记录用户是否在线,在线则不允许登录

注销:删除好友信息,删除个人信息,删除网盘文件。

目录

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

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值