物联网实战--平台篇之(四)账户后台交互

目录

一、交互逻辑

二、请求验证码

三、帐号注册

四、帐号/验证码登录

五、重置密码


本项目的交流QQ群:701889554

物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html

物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html

本项目资源文件https://download.csdn.net/download/ypp240124016/89280540

一、交互逻辑

对于账户的注册、登录等流程的交互逻辑基本上是这样的:

1、用户端APP提交相关信息;

2、服务器接收解析信息,同时对信息的合法性进行认证;

3、根据命令类型执行对应的操作;

4、返回操作结果;

5、用户端APP显示结果并进入下一步。

两个工程都有跟账户相关的类文件,核心就是对这个流程中的不同指令进行处理,接下来根据指令详细看下处理流程。

二、请求验证码

先看下图箭头所指的三个关键信息,这是手机APP的代码,刚开始的时候会获取设备的mac地址和一个随机数,然后把他们作为订阅话题的组成部分,这样服务器就可以根据上报携带的身份信息进行针对性地回应了,这个订阅话题示例:yyy125/as/pub/account/E0:23:A3:62:63:E2/1629/#

这样基本上具备唯一性了。第三个箭头是账户相关的前后端交互接口,QML文件中可以直接用theAccountMan调用AccountMan类中的后端函数。

接下来进入用户端APP的AccountMan类中,核心是下图框框内的几个函数,分别是请求验证码、请求注册、请求账户登录、请求验证码登录和请求重置密码,可以看出,所有流程都是以APP端请求开始的。其它是对字符串进行有效性验证的函数,比如手机号、账户名和密码的格式进行校验,便于前端检测输入的合法性。

cpp 复制代码
void AccountMan::requestVerCode(QString account, QString phone)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;

    root_obj.insert("cmd_type", "req_vercode");
    root_obj.insert("account", account);
    root_obj.insert("phone", phone);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("code");
    emit sigMqttPushMessage(topic, msg_ba);
}


QString AccountMan::makePubTopic(QString key_str)
{
    QString topic=QString(TOPIC_HEAD)+"as/sub/account/"+key_str;
    return  topic;
}

以上是请求验证码的代码,参数账户名和手机号由前端输入,其中账户名可以为空,保留功能,核心是要手机号,然后用json的形式组合参数,命令类型是req_vercode,随机数和mac地址是主程序中传进来的,服务器就是根据这两个信息组合返回的发布话题的,这样才能收到返回结果。在这里还有个组合发布话题的函数,现在传入的是code关键字,组合后就是yyy125/as/sub/account/code,字面上就能理解了这个话题的数据是发送给应用服务器的,内容是账户相关的,子功能为验证码,至于具体要干嘛那就交给数据包内的cmd_type字段去处理了,对于验证码功能主要就是 "请求验证码" 命令了。

数据发送到服务器后,主程序先根据话题筛选出账户类话题,然后把该类型话题的数据转发到账户线程中去进一步处理,代码如下:

cpp 复制代码
void MainInterface::slotMqttReceived(const QMQTT::Message &message)
{
    QJsonParseError json_error;
    QJsonDocument json_doc;
    QString recv_topic=message.topic();
     qDebug()<<"msg topic= "<<message.topic();
     qDebug()<<message.payload().data();
    json_doc = QJsonDocument::fromJson(message.payload(), &json_error);//转为JSON格式
    if(json_error.error != QJsonParseError::NoError)
    {
//        qDebug()<<"json error= "<<json_error.error;
        return;
    }
    QJsonObject rootObj = json_doc.object();
    
    if(recv_topic.contains("account/"))//账户类相关的话题
    {
        emit sigAccountThreadMessage(recv_topic, rootObj);
    }
    
}

进入账户线程后代码如下,根据话题的子功能进行分类处理,对于验证码类型,我们刚才组合话题时候添加的关键字是code,那么话题内有/code的就进行具体的验证码数据解析,具体函数是parseVerCodeTopic(),在函数内,我们获取了命令类型、手机号、mac地址和随机数等数据,命令类型目前只有请求验证码,对于此命令的处理步骤是:

1、检查手机号;

2、获取随机验证码;

3、缓存验证码;

4、发送验证码;

5、回复用户端APP

其中,步骤3在缓存时会检查该手机号之前是否有验证码存在,如果有的话就缓存失败,同时也会回复用户端失败的信息;验证码的添加成功后会在一个列表内暂存30秒,超时后自动删除;步骤4中的发送验证码,理论上需要SMS服务器的,这个是需要企业才能办理的业务,我们当前先打印出来就好,实际的发送后面再专门出一篇。

cpp 复制代码
void AccountThread::slotAccountThreadMessage(QString topic, QJsonObject root_obj)
{
    if(topic.contains("/reg"))//注册相关
    {
        parseRegTopic(root_obj);
    }
    else if(topic.contains("/login"))//登录相关
    {
        parseLoginTopic(root_obj);
    }
    else if(topic.contains("/code"))//验证码相关
    {
        parseVerCodeTopic(root_obj);
    }
    else if(topic.contains("/passwd"))//密码相关
    {
        parsePasswdTopic(root_obj);
    }
    else if(topic.contains("/child"))//子账户相关
    {
        
    }
    else if(topic.contains("/app"))//应用相关
    {
        
    }
    else if(topic.contains("/group"))//分组相关
    {
        
    }
    else if(topic.contains("/device"))//设备相关
    {
        
    }
}



//解析验证码话题
void AccountThread::parseVerCodeTopic(QJsonObject root_obj)
{
    QString cmd_type="";
    if(root_obj.contains("cmd_type"))//命令类型
    {
        QJsonValue value = root_obj.value("cmd_type");
        if(value.isString())
        {
            cmd_type=value.toString();
        }
    }
    QString account="";
    if(root_obj.contains("account"))//账户
    {
        QJsonValue value = root_obj.value("account");
        if(value.isString())
        {
            account=value.toString();
        }
    }

    int rand_num=0;
    if(root_obj.contains("rand_num"))//随机数
    {
        QJsonValue value = root_obj.value("rand_num");
        if(value.isDouble())
        {
            rand_num=(int)value.toDouble();
        }
    }
    QString mac_str="";
    if(root_obj.contains("mac"))//mac
    {
        QJsonValue value = root_obj.value("mac");
        if(value.isString())
        {
            mac_str=value.toString();
        }
    }
    QString phone="";
    if(root_obj.contains("phone"))//手机号码
    {
        QJsonValue value = root_obj.value("phone");
        if(value.isString())
        {
            phone=value.toString();
        }
    }
    
    if(cmd_type=="req_vercode")//请求验证码
    {
        if(phone.isEmpty())
        {
            qDebug()<<"phone.isEmpty()";
            return;
        }
        QString ver_code_str=takeVerCode();
        bool ok=addReqVerCodeNode(account, phone, ver_code_str);//添加进列表,进行超时检测
        if(ok)
        {
            //向SMS服务器发送验证码
//            sendSmsCheckCode(phone, ver_code_str);
            ackReqVerCodeState(account, mac_str, rand_num, phone, 0, "验证码已发送!");//返回验证码已发送状态
            qDebug()<<"req_vercode ok";
        }
        else
        {
            ackReqVerCodeState(account, mac_str, rand_num, phone, 1, "重复发送!");//
        }

    }
}

回复函数根据不同的命令类型参数略有区别,下面是请求验证码的函数,注意点就是回复的话题,需要根据上传的mac、随机数和关键字段组合,与开头就形成闭环了。

cpp 复制代码
void AccountThread::ackReqVerCodeState(QString account, QString mac_str, int rand_num, QString phone, int result, QString ack_str)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("cmd_type", "req_vercode");
    root_obj.insert("result", result);
    root_obj.insert("phone", phone);
    root_obj.insert("ack_str", ack_str);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic(account, mac_str, rand_num, "code");
    emit sigMqttPushMessage(topic, msg_ba);
}


//生成发布话题
QString AccountThread::makePubTopic(QString account, QString mac_str, int rand_num, QString key_str)
{
    if(account.isEmpty())
    {
        
    }
    QString topic=QString(TOPIC_HEAD) + SERVER_PUB_TOPIC+QString("/account/")+mac_str+QString::asprintf("/%d/", rand_num)+key_str;
    return topic;
}

回到用户端这边,主程序也是根据话题类型归类处理的,目前只进行简单的信息提示处理,就是将服务器的回复信息发送到QML前端进行显示交互。

总的来讲,整个流程就是这样了,其他注册、登录等功能也是类似的。

三、帐号注册

1、用户端发送注册信息,包含账户、密码、手机、验证码等:

cpp 复制代码
void AccountMan::requestReg(QString account, QString passwd, QString phone, QString ver_code)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;

    root_obj.insert("cmd_type", "req_reg");
    root_obj.insert("account", account);
    root_obj.insert("pass_word", passwd);
    root_obj.insert("phone", phone);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr);
    root_obj.insert("ver_code", ver_code);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("reg");
    emit sigMqttPushMessage(topic, msg_ba);
}

2、服务器接收解析,在这里要对账户和手机号的重复性进行检查,注册成功后会默认新建一个app_id。

cpp 复制代码
//解析注册话题
void AccountThread::parseRegTopic(QJsonObject root_obj)
{
    QString cmd_type="";
    if(root_obj.contains("cmd_type"))//命令类型
    {
        QJsonValue value = root_obj.value("cmd_type");
        if(value.isString())
        {
            cmd_type=value.toString();
        }
    }
    QString account="";
    if(root_obj.contains("account"))//账户
    {
        QJsonValue value = root_obj.value("account");
        if(value.isString())
        {
            account=value.toString();
        }
    }

    int rand_num=0;
    if(root_obj.contains("rand_num"))//随机数
    {
        QJsonValue value = root_obj.value("rand_num");
        if(value.isDouble())
        {
            rand_num=(int)value.toDouble();
        }
    }
    QString mac_str="";
    if(root_obj.contains("mac"))//mac
    {
        QJsonValue value = root_obj.value("mac");
        if(value.isString())
        {
            mac_str=value.toString();
        }
    }
    QString pass_word="";
    if(root_obj.contains("pass_word"))//密码
    {
        QJsonValue value = root_obj.value("pass_word");
        if(value.isString())
        {
            pass_word=value.toString();
        }
    }
    QString phone="";
    if(root_obj.contains("phone"))//手机号码
    {
        QJsonValue value = root_obj.value("phone");
        if(value.isString())
        {
            phone=value.toString();
        }
    }
    QString ver_code="";
    if(root_obj.contains("ver_code"))//验证码
    {
        QJsonValue value = root_obj.value("ver_code");
        if(value.isString())
        {
            ver_code=value.toString();
        }
    }
    
    if(cmd_type=="req_reg")//请求注册
    {
        bool ok;
        account.toDouble(&ok);
        if(ok)
        {
            ackReqRegState(account, mac_str, rand_num, phone, 6, "账户不能为纯数字!");
            return;
        }
        
        AccountSqlite::AccountNodeStruct tag_account;
        bool ok1, ok2;
        ok1=m_accountSqlite->searchAccountByName(account, tag_account);
        ok2=m_accountSqlite->searchAccountByPhone(phone, tag_account);
        if(ok1==false && ok2==false)//未找到重复的,可以注册
        {
            for(auto iter : m_reqVerCodeList)
            {
                if(iter.phone == phone)//根据手机号检索
                {
                    if(iter.verCode == ver_code)//验证码相等
                    {
                        bool ok=m_accountSqlite->addAccountNode(account, pass_word, 0x00, "", phone);
                        if(ok)
                        {
                            ackReqRegState(account, mac_str, rand_num, phone, 0, "注册成功!");
                            qDebug()<<"reg ok!";
                            //自动创建一个应用
                            u32 max_app=m_accountSqlite->selectMaxAppID();
                            u32 new_app_id=0;
                            if(max_app>APP_ID_MIN)
                                new_app_id=max_app+1;
                            else  
                                new_app_id=APP_ID_MIN+1;
                            
                            qDebug()<<"new_app_id="<<new_app_id;
                           m_accountSqlite->addAppIDToList(new_app_id, account);
                        }
                        else
                        {
                            ackReqRegState(account, mac_str, rand_num, phone, 1, "数据库存储出错!");
                            qDebug()<<"reg sql error!!";
                        }
                    }
                    else
                    {
                        ackReqRegState(account, mac_str, rand_num, phone, 2, "验证码错误!");
                        qDebug()<<"reg ver_code error!";
                    }
                    return;
                }
            }
            ackReqRegState(account, mac_str, rand_num, phone, 3, "验证码超时!");
            qDebug()<<"no found code!";
        }
        else
        {
            if(ok1==true)
            {
                ackReqRegState(account, mac_str, rand_num, phone, 4, "账户名已存在!");
                qDebug()<<"have same account="<<account;
            }
            else if(ok2==true)
            {
                ackReqRegState(account, mac_str, rand_num, phone, 5, "手机号已存在!");
                qDebug()<<"have same phone="<<phone;
            }
        }
    }
}
四、帐号/验证码登录

1、用户发送帐号登录信息,主要包括账户和密码:

cpp 复制代码
void AccountMan::requestLogin(QString account, QString pass_word, int remember)
{
    if(remember){}
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("cmd_type", "login_pwd");
    root_obj.insert("account", account);
    root_obj.insert("pass_word", pass_word);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr); 
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("login");
    emit sigMqttPushMessage(topic, msg_ba);
}

如果是验证码登录,那就是手机号和验证码

cpp 复制代码
void AccountMan::requestLoginByVerCode(QString phone, QString ver_code)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("cmd_type", "login_code");
    root_obj.insert("phone", phone);
    root_obj.insert("ver_code", ver_code);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr); 
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("login");
    emit sigMqttPushMessage(topic, msg_ba);
}

2、服务端接收解析,登录操作要仔细校验账户名和密码,账户也可以填手机号,服务端会自己判断;验证码登录的时候,如果该手机号未注册则会自动注册,用户名默认是Y+手机号,密码随机,需要自己去重置。

cpp 复制代码
//解析登录话题
void AccountThread::parseLoginTopic(QJsonObject root_obj)
{
    QString cmd_type="";
    if(root_obj.contains("cmd_type"))//命令类型
    {
        QJsonValue value = root_obj.value("cmd_type");
        if(value.isString())
        {
            cmd_type=value.toString();
        }
    }
    QString account="";
    if(root_obj.contains("account"))//账户
    {
        QJsonValue value = root_obj.value("account");
        if(value.isString())
        {
            account=value.toString();
        }
    }

    int rand_num=0;
    if(root_obj.contains("rand_num"))//随机数
    {
        QJsonValue value = root_obj.value("rand_num");
        if(value.isDouble())
        {
            rand_num=(int)value.toDouble();
        }
    }
    QString mac_str="";
    if(root_obj.contains("mac"))//mac
    {
        QJsonValue value = root_obj.value("mac");
        if(value.isString())
        {
            mac_str=value.toString();
        }
    }
    QString pass_word="";
    if(root_obj.contains("pass_word"))//密码
    {
        QJsonValue value = root_obj.value("pass_word");
        if(value.isString())
        {
            pass_word=value.toString();
        }
    }
    QString phone="";
    if(root_obj.contains("phone"))//手机号码
    {
        QJsonValue value = root_obj.value("phone");
        if(value.isString())
        {
            phone=value.toString();
        }
    }
    QString ver_code="";
    if(root_obj.contains("ver_code"))//验证码
    {
        QJsonValue value = root_obj.value("ver_code");
        if(value.isString())
        {
            ver_code=value.toString();
        }
    }
    
    AccountSqlite::AccountNodeStruct tag_account;
    if(cmd_type=="login_pwd")//密码登录
    {
        if(!account.isEmpty())
        {                        
            bool ok=account.toDouble();
            if(ok)//手机号登录
            {
                QString phone=account;
                if( m_accountSqlite->selectAccountByPhone(phone, tag_account)==false)
                {
                    ackLoginState("", phone, "", mac_str, rand_num, 0, "", 1, "手机号未注册!");
                    return;
                }
            }
            else
            {
                if( m_accountSqlite->selectAccountByName(account, tag_account)==false)
                {
                    ackLoginState(account, "", "", mac_str, rand_num, 0, "", 2, "帐号未注册!");
                    return;
                }
            }
            if((account==tag_account.account || account==tag_account.phone) && pass_word==tag_account.passWord)//再次校验用户名跟密码
            {
               ackLoginState(tag_account.account,tag_account.phone, tag_account.parentAccount, mac_str, rand_num, tag_account.auth, tag_account.createTime, 0, "登录成功!");
            }
            else
            {
                ackLoginState(tag_account.account,tag_account.phone, tag_account.parentAccount, mac_str, rand_num, tag_account.auth, tag_account.createTime, 3, "密码错误!");
            }
        }
        else
        {
            ackLoginState(account, "", "", mac_str, rand_num, 0, "", 4, "账户不存在!");
        }
    }
    else if(cmd_type=="login_code")//验证码登录
    {
        bool ok; 
        phone.toDouble(&ok);
        if(phone.size()!=11 || !ok)
        {
            qDebug()<<"login phone="<<phone<<" error!";
            ackReqRegState(phone, mac_str, rand_num, phone, 1, "手机号有误!");
            return;
        }
        bool flag=false;
        for(auto iter : m_reqVerCodeList)
        {
            if(iter.phone == phone)
            {
                if(iter.verCode == ver_code)
                {
                    flag=true;
                }
                break;
            }
        }
        if(flag==false)
        {
            ackReqRegState(phone, mac_str, rand_num, phone, 1, "验证码错误!");
            return;
        }
        
        tag_account.phone.clear();
        m_accountSqlite->searchAccountByPhone(phone, tag_account);
        if(phone==tag_account.phone)
        {
            ackReqRegState(phone, mac_str, rand_num, phone, 0, "登录成功!");
        }
        else//新手机号,直接注册
        {
            account="Y"+phone;
            pass_word.clear();
            for(int i=0; i<8; i++)
            {
                pass_word+=QString::asprintf("%d", drv_com.takeRandNumber()%10);//随机密码
            }
            qDebug()<<account<<" pass_word="<<pass_word;
            bool ok=m_accountSqlite->addAccountNode(account, pass_word, 0x00, "", phone);
            if(ok)
            {
                ackReqRegState(phone, mac_str, rand_num, phone, 0, "登录(注册)成功!");
                qDebug()<<"reg ok!";
                //自动创建一个应用
                u32 max_app=m_accountSqlite->selectMaxAppID();
                u32 new_app_id=0;
                if(max_app>APP_ID_MIN)
                    new_app_id=max_app+1;
                else  
                    new_app_id=APP_ID_MIN+1;
                
                qDebug()<<"phone new_app_id="<<new_app_id;
               m_accountSqlite->addAppIDToList(new_app_id, account);
            }
            else
            {
                ackReqRegState(phone, mac_str, rand_num, phone, 1, "数据库存储错误!");
                qDebug()<<"reg sql error!!";
            }
        }
    }
    
}
五、重置密码

1、用户发送重置信息主要包括用户名、新密码、手机号和校验码:

cpp 复制代码
void AccountMan::requestResetPasswd(QString account, QString pass_word, QString phone, QString ver_code)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("cmd_type", "reset_pwd");
    root_obj.insert("account", account);
    root_obj.insert("pass_word", pass_word);
    root_obj.insert("phone", phone);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr);
    root_obj.insert("ver_code", ver_code);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("passwd");
    emit sigMqttPushMessage(topic, msg_ba);
}

2、服务端接收解析,首先要检查与数据库里的账户和手机是否匹配,然后校验验证码,通过后就可以更新密码了,为了确保写入成功,需要再读取出来进行对比,新密码校验成功后才算真正完成了密码重置。

cpp 复制代码
void AccountThread::parsePasswdTopic(QJsonObject root_obj)
{
    QString cmd_type="";
    if(root_obj.contains("cmd_type"))//命令类型
    {
        QJsonValue value = root_obj.value("cmd_type");
        if(value.isString())
        {
            cmd_type=value.toString();
        }
    }
    QString account="";
    if(root_obj.contains("account"))//账户
    {
        QJsonValue value = root_obj.value("account");
        if(value.isString())
        {
            account=value.toString();
        }
    }
    QString mac_str="";
    if(root_obj.contains("mac"))//mac
    {
        QJsonValue value = root_obj.value("mac");
        if(value.isString())
        {
            mac_str=value.toString();
        }
    }

    int rand_num=0;
    if(root_obj.contains("rand_num"))//rand_num
    {
        QJsonValue value = root_obj.value("rand_num");
        if(value.isDouble())
        {
            rand_num=(int)value.toDouble();
        }
    }
    QString pass_word="";
    if(root_obj.contains("pass_word"))//密码
    {
        QJsonValue value = root_obj.value("pass_word");
        if(value.isString())
        {
            pass_word=value.toString();
        }
    }
    QString phone="";
    if(root_obj.contains("phone"))//手机号码
    {
        QJsonValue value = root_obj.value("phone");
        if(value.isString())
        {
            phone=value.toString();
        }
    }
    QString ver_code="";
    if(root_obj.contains("ver_code"))//验证码
    {
        QJsonValue value = root_obj.value("ver_code");
        if(value.isString())
        {
            ver_code=value.toString();
        }
    }
    
    if(cmd_type=="reset_pwd")
    {
        if(account.isEmpty() || phone.isEmpty())
        {
            qDebug()<<"account.isEmpty() || phone.isEmpty()";
            return;
        }
        AccountSqlite::AccountNodeStruct tag_account;
        m_accountSqlite->selectAccountByPhone(phone, tag_account);
        if(tag_account.account!=account)
        {
            ackResetPasswdState(account, mac_str, rand_num, 4, "账户与手机不匹配!");
            return;
        }
                
        for(auto iter : m_reqVerCodeList)
        {
            if(iter.phone == phone)
            {
                if(iter.verCode == ver_code)
                {
                    tag_account.passWord.clear();
                    m_accountSqlite->updateAccountPassWord(account, pass_word);//更新密码
                    m_accountSqlite->selectAccountByName(account, tag_account);//重新获取密码
                    if(tag_account.passWord==pass_word)//新密码校验
                    {
                        ackResetPasswdState(account, mac_str, rand_num, 0, "密码修改成功!");
                        qDebug("set new pwd ok!");
                    }
                    else
                    {
                        ackResetPasswdState(account, mac_str, rand_num, 1, "密码修改(校验)失败!");
                        qDebug("set new pwd failed!");
                    }
                }
                else
                {
                    ackResetPasswdState(account, mac_str, rand_num, 2, "验证码错误!");
                    qDebug()<<"reg ver_code error!";
                }
                return;
            }
        }
        ackResetPasswdState(account, mac_str, rand_num, 3, "验证码已过期!");
        
    }
    
}

服务端经常有数据库操作步骤,这里再看下数据库的创建和打开,因为账户管理任务是独立的线程,所以我这里数据库定义为指针类型,然后在槽函数里new一个 AccountSqlite(),这样数据库操作才属于线程内部,这点很重要,这是QT的特性;然后就是打开数据库,参数就是文件名(包括路径)+连接名称,如果文件不存在就会自动创建,连接名称随意,这里一般不会重复;最后就是创建账户表和应用表了,之前数据库语句介绍过了,这样操作并不会重复创建库表。

相关推荐
BAGAE4 小时前
MODBUS 通信协议详细介绍
linux·嵌入式硬件·物联网·硬件架构·iot·嵌入式实时数据库·rtdbs
jz-炸芯片的zero15 小时前
【Zephyr电源与功耗专题】14_BMS电池管理算法(三重验证机制实现高精度电量估算)
单片机·物联网·算法·zephyr·bms电源管理算法
亿坊电商1 天前
物联网-无人自助茶室-如何实现24H智能营业?
物联网
TDengine (老段)1 天前
TDengine 选择函数 TOP() 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
御控工业物联网1 天前
智慧灌溉泵房远程监控物联网系统解决方案
物联网·远程监控·组态监控·智慧水务·智慧灌溉·无人值守泵站·设备远程调试
御控工业物联网1 天前
农田水利工程远程监控与远程调试的御控物联网系统解决方案
物联网·远程监控·远程调试
清风6666661 天前
基于STM32单片机的OneNet物联网粉尘烟雾检测系统
stm32·单片机·物联网·毕业设计·课程设计
TDengine (老段)1 天前
TDengine 特殊函数 MODE() 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
余衫马2 天前
开发指南:使用 MQTTNet 库构建 .Net 物联网 MQTT 应用程序
物联网·mqtt·.net
御控工业物联网2 天前
城市二次供水物联网监测管控管理平台御控解决方案:构建全链路智能水务新生态
物联网·数据采集·远程监控·物联网网关·二次供水·智能水务·泵站