物联网实战--入门篇之(十)安卓QT--后端开发

目录

一、项目配置

二、MQTT连接

三、数据解析

四、数据更新

五、数据发送

六、指令下发


一、项目配置

        按常规新建一个Quick空项目后,我们需要对项目内容稍微改造、规划下。

        首先根据我们的需要在.pro文件内添加必要的模块,其中quick就是qml了,core那是核心模块,必须的,network是网络模块,MQTT需要使用网络。

        然后就是为QML文件和图片文件各自建立一个资源文件,这样编译的时候会把这些资源带上,否则的话打包发布的时候你需要把QML文件和图片文件放到指定文件夹内,这在安卓里就不方便了。

        最后就是如何加载前端QML文件的问题了,如下图所示,后端通过QML加载引擎QQmlApplicationEngine把QML主文件加载进来就能显示界面了,下一行是前端到后端的设置接口名称,这样在QML文件里就可以用theMainInterface这个名称引用后端的函数了,完成开关、调速等动作。

        在新工程默认的文件里,加载前端文件这一步是在main.c文件里完成的,我们这里为了整体前后端的交互,特意建了一个MainInterface的类,在主函数中直接定义这个类的变量即可,这样整个工程结构比较清晰。

二、MQTT连接

        QT标准库里没有mqtt,需要自己单独下载GitCode - 开发者的代码家园,我的项目里已经集成了,只要右键添加进来即可,这里主要是要写个自己的MQTT管理类,把状态保活、话题订阅等任务内部处理掉,就是BaseMqtt类了,里面还有个内容是域名解析需要处理。

#include "BaseMqtt.h"


BaseMqtt::BaseMqtt(QObject *parent) : QObject(parent)
{
    isConnected=false;
    checkTimer = new QTimer(this);
    checkTimer->setInterval(1*100);//心跳检测
    checkTimer->start();
    m_heartTime=0;
    connect(checkTimer, SIGNAL(timeout()),this,SLOT(slotCheckTimeout()));

    m_mqttClient=nullptr;
    m_hostAddress="";
}
BaseMqtt::~BaseMqtt(void)
{
    qDebug()<<"~BaseMqtt, hostName="<<m_connectParam.hostName;
}

void BaseMqtt::slotLookUpHost(QHostInfo info)
{
   if(info.error() == QHostInfo::NoError)
   {
       foreach (QHostAddress address, info.addresses())
       {
            m_hostAddress=address.toString();
            qDebug()<<m_connectParam.hostName<<" mqtt found ip= "<<m_hostAddress;
            break;
       }
   }
}

void BaseMqtt::slotCheckTimeout(void)
{
    m_heartTime++;
    if(m_hostAddress.isEmpty())
    {
        if(m_heartTime%10==0)
        {
            qDebug("mqtt start dns!");
            QHostInfo::lookupHost(m_connectParam.hostName, this, SLOT(slotLookUpHost(QHostInfo)));
        }
        return;
    }
    if(m_mqttClient==nullptr)
    {
        if(!m_connectParam.certPath.isEmpty())//使用SSL连接
        {
            QFile file;
            QByteArray client_key_text, client_crt_text, root_crt_text;
            QString certPath=m_connectParam.certPath;
            QSslSocket ssl_socket;
            
            file.setFileName(certPath+"/client.key");
            file.open(QIODevice::ReadOnly | QIODevice::Text);
            client_key_text = file.readAll();
            file.close();
            
            file.setFileName(certPath+"/client.crt");
            file.open(QIODevice::ReadOnly | QIODevice::Text);
            client_crt_text = file.readAll();
            file.close();

            file.setFileName(certPath+"/rootCA.crt");
            file.open(QIODevice::ReadOnly | QIODevice::Text);
            root_crt_text = file.readAll();
            file.close();
            
            QSslKey client_key(client_key_text, QSsl::Rsa);
            QSslCertificate client_crt(client_crt_text);            

            ssl_socket.setPrivateKey(client_key);
            ssl_socket.setLocalCertificate(client_crt);
            
            QSslConfiguration ssl_config=QSslConfiguration::defaultConfiguration();
            ssl_config.setCaCertificates(QSslCertificate::fromData(root_crt_text)); //QSslCertificate::fromPath(certPath+"/rootCA.crt"); 
            ssl_config.setPrivateKey(ssl_socket.privateKey());
            ssl_config.setLocalCertificate(ssl_socket.localCertificate());
            ssl_config.setPeerVerifyMode(QSslSocket::QueryPeer);
            ssl_config.setPeerVerifyDepth(10);
            ssl_config.setProtocol(QSsl::TlsV1_2);

            m_mqttClient = new QMQTT::Client(m_hostAddress, m_connectParam.hostPort, ssl_config, true, this);
//            qDebug()<<"\n###SSL PrivateKey="<<ssl_config.privateKey();
//            qDebug()<<"###SSL Certificate="<<ssl_config.localCertificate();
//            qDebug()<<"###SSL rootCA="<<ssl_config.caCertificates();
//            qDebug()<<"hostName="<<m_hostAddress<<", hostPort="<<m_connectParam.hostPort;
//            qDebug()<<"userName="<<m_connectParam.userName<<", passWord="<<m_connectParam.passWord<<", clientID="<<m_connectParam.clientID;
        }
        else//普通连接
        {
            QHostAddress host(m_hostAddress);
            m_mqttClient = new QMQTT::Client(host, m_connectParam.hostPort, this);
        }
        signalSlotInit();
        m_mqttClient->setUsername(m_connectParam.userName);
        m_mqttClient->setPassword(m_connectParam.passWord);
        m_mqttClient->setClientId(m_connectParam.clientID);
        m_mqttClient->setAutoReconnect(true);
        m_mqttClient->setCleanSession(true);
        m_mqttClient->setKeepAlive(30);
        m_mqttClient->connectToHost();
    }
    else if(this->mqttIsConnected())
    {
        for(auto iter : m_subTopicList)//订阅话题
        {
            if(iter.isSubed==false)
            {
                mqttSubscribeMessage(iter.subTopic, iter.qos);
                break;
            }
        }
        if(m_heartTime%200==0)//保持连接
        {
//            qDebug()<<"mqtt send keep";
            mqttPublishMessage("dev/sub/data1", QByteArray("heart"));
        }
    }
    else
    {
//        qDebug()<<"BaseMqtt no connected!";
    }
}

void BaseMqtt::mqttConnect(QString hostName, u16 hostPort, QString userName, QByteArray passWord, QString clientID, QString certPath)
{
    clientID=clientID+QString("_")+takeHostMac().remove(":");
    m_connectParam.hostName=hostName;
    m_connectParam.hostPort=hostPort;
    m_connectParam.userName=userName;
    m_connectParam.passWord=passWord;
    m_connectParam.clientID=clientID;
    m_connectParam.certPath=certPath;

    u8 *pData=(u8*)hostName.toUtf8().data();
    if(pData[0]>='0' && pData[0]<='9')//判断是否为域名,使用域名时 域名的第一个字符不能是数字
    {
        m_hostAddress=hostName;
    }
}

bool BaseMqtt::mqttIsConnected(void)
{
//    if(m_mqttClient!=nullptr)
//        return m_mqttClient->isConnectedToHost();
    return isConnected;
}

void BaseMqtt::mqttPublishMessage(QString topicFilter, QByteArray msgBa)
{
    if(m_mqttClient==nullptr || m_mqttClient->isConnectedToHost()==false)
       return;
    QMQTT::Message message;
    message.setTopic(topicFilter);
    message.setPayload(msgBa);
    m_mqttClient->publish(message);
}

void BaseMqtt::mqttPingresp(void)
{
//    m_mqttClient->pingresp();
}

void BaseMqtt::mqttSubscribeMessage(QString topicFilter, quint8 qos)
{
    if(m_mqttClient==nullptr)
       return;
    m_mqttClient->subscribe(topicFilter, qos);
}

void BaseMqtt::mqttUnsubscribeMessage(QString topicFilter)
{
    if(m_mqttClient==nullptr)
       return;
    m_mqttClient->unsubscribe(topicFilter);
}

void BaseMqtt::signalSlotInit(void)
{
    connect(m_mqttClient, SIGNAL(connected()), this, SLOT(slotMqttConnected()));
    connect(m_mqttClient, SIGNAL(disconnected()), this, SLOT(slotMqttDisconnected()));
    connect(m_mqttClient, SIGNAL(error(QMQTT::ClientError)), this, SLOT(slotMqttError(QMQTT::ClientError)));
    connect(m_mqttClient, SIGNAL(pingresp()), this, SLOT(slotMqttPingresp()));
    connect(m_mqttClient, SIGNAL(published(QMQTT::Message,quint16)), this, SLOT(slotMqttPuslished(QMQTT::Message,quint16)));
    connect(m_mqttClient, SIGNAL(received(QMQTT::Message)), this, SLOT(slotMqttReceived(QMQTT::Message)));
    connect(m_mqttClient, SIGNAL(subscribed(QString,quint8)), this, SLOT(slotMqttSubscribed(QString,quint8)));
    connect(m_mqttClient, SIGNAL(unsubscribed(QString)), this, SLOT(slotMqttUnsubscribed(QString)));
}

void BaseMqtt::mqttAddTopic(QString topic, u8 qos)
{
    for(auto iter : m_subTopicList)
    {
        if(iter.subTopic==topic)
        {
            qDebug()<<"have the same topic="<<topic;
            return;
        }
    }
    SubTopicStruct tag_subTopic;
    tag_subTopic.subTopic=topic;
    tag_subTopic.isSubed=false;
    tag_subTopic.qos=qos;
    m_subTopicList.append(tag_subTopic);
    qDebug()<<"mqttAddTopic="<<topic;
}

void BaseMqtt::mqttDelTopic(QString topic)
{
    int i=0;
    for(auto iter : m_subTopicList)
    {
        if(topic==iter.subTopic)
        {
            if(iter.isSubed==true)
            {
                this->mqttUnsubscribeMessage(topic);
            }
            m_subTopicList.removeAt(i);
            qDebug()<<"remove topic="<<topic;
            return;
        }
        i++;
    }
}

void BaseMqtt::slotMqttConnected(void)
{
    isConnected=true;
    emit sigMqttConnected();
    qDebug()<<"clientId="<<m_mqttClient->clientId()<<"connected";
}

void BaseMqtt::slotMqttDisconnected(void)
{
    isConnected=false;
    int nSize=m_subTopicList.size();
    for(int i=0; i<nSize; i++)
    {
        m_subTopicList[i].isSubed=false;
    }
    qDebug()<<"clientId="<<m_mqttClient->clientId()<<"disconnected";
    emit sigMqttDisconnected();
}

void BaseMqtt::slotMqttError(const QMQTT::ClientError error)
{
    qDebug()<<"clientId="<<m_mqttClient->clientId()<<"slotMqttError="<<error;
}

void BaseMqtt::slotMqttPingresp(void)
{
//    qDebug("BaseMqtt::slotMqttPingresp");
}

void BaseMqtt::slotMqttPuslished(const QMQTT::Message &message, quint16 msgid)
{
    msgid=message.id();
    msgid=msgid;
//    qDebug("BaseMqtt::slotMqttPuslished, msgid=%d ", msgid);
//    qDebug()<<"msg="<<message.payload().toHex();
}

void BaseMqtt::slotMqttReceived(const QMQTT::Message &message)
{
    emit sigtMqttReceived(message);

}

void BaseMqtt::slotMqttSubscribed(const QString &topic, const quint8 qos)
{
    int i=0;
//    qDebug()<<"slotMqttSubscribed, topic="<<topic<<", qos="<<qos;
    for(auto iter : m_subTopicList)
    {
        if(iter.subTopic==topic)
        {
            m_subTopicList[i].isSubed=true;
            break;
        }
        i++;
    }
    emit sigMqttSubscribed(topic, qos);
}

void BaseMqtt::slotMqttUnsubscribed(const QString &topic)
{
    int i=0;
    for(auto iter : m_subTopicList)
    {
        if(iter.subTopic==topic)
        {
            m_subTopicList[i].isSubed=false;
            break;
        }
        i++;
    }
    emit sigMqttUnsubscribed(topic);
}

QString BaseMqtt::takeHostMac(void)
{
    DrvCommon drv_com;    
    return drv_com.takeRandMac();
}


        对于应用层就很简单了,就是创建对象、连接和添加订阅话题即可。其中有个槽函数slotMqttReceived就是用来接收设备发来的数据的。

三、数据解析

        数据解析跟嵌入式端是差不多的,下面是代码,像剥洋葱一样,查找帧头、校验、根据命令类型执行解析。


void MainInterface::slotMqttReceived(const QMQTT::Message &message)
{
    QByteArray msg_ba=message.payload();
    u8 *pData=(u8*)msg_ba.data();
//    qDebug()<<"mqtt recv="<<msg_ba.toHex(':');
    u8 head[2]={0xAA, 0x55};
   pData=drv_com.memstr(pData, msg_ba.size(), head, 2);
    if(pData!=nullptr)
    {
      u16 total_len=pData[2]<<8 | pData[3];
      u16 crcValue=pData[total_len]<<8 | pData[total_len+1];
      if(crcValue==drv_com.crc16(pData, total_len))
      {
        pData+=4;
        u32 device_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
        pData+=4;
        m_currDevSn=device_sn;
        u8 cmd_type=pData[0];
        pData+=1;
        qDebug("recv device_sn=%08X, cmd_type=%d", device_sn, cmd_type);
        m_keepTime=m_secCounts;
        switch(cmd_type)
        {
            case AIR_CMD_HEART:
            {
    
                break;
            }
            case AIR_CMD_DATA:
            {
                int temp=pData[0]<<8|pData[1];//温度 原始数据
                float temp_f=(temp-1000)/10.f;//温度浮点数据
                pData+=2;
                int humi=pData[0]<<8|pData[1];
                float humi_f=humi/10.f;
                pData+=2;
                int pm25=pData[0]<<8|pData[1];
                pData+=2;
                u8 speed=pData[0];
                pData+=1;
                u8 state=pData[0];
                pData+=1;
                qDebug("temp_f=%.1f C, humi_f=%.1f%%, pm25=%d ug/m3, speed=%d, state=%d", temp_f,  humi_f, pm25, speed,state);
                QString dev_sn_str=QString::asprintf("%08X", device_sn);
                QString temp_str=QString::asprintf("%.0f", temp_f);
                QString humi_str=QString::asprintf("%.0f", humi_f);
                QString pm25_str=QString::asprintf("%03d", pm25);
                
                int alarm_level=0;
                if(pm25<20)alarm_level=0;
                else if(pm25<30)alarm_level=1;
                else alarm_level=2;
                
                emit siqUpdateSensorValues(dev_sn_str, temp_str, humi_str, pm25_str);
                emit siqUpdateAlarmLevel(alarm_level);
                emit siqUpdateSwitchState(state);
                break;
            }
            case AIR_CMD_SET_SPEED:
            {
    
                break;
            }
        }
      }
    }
}

        这里我们需要把数据送到前端去显示,所以定义了几个信号内容,如下图所示,从上到下依次是状态数据,污染等级和开关状态,这些数据都是设备端发送上来的,通过后端处理加工后发到前端显示。这里对于污染等级的数值可以自定义,我这边为了方便测试是20、30两个分界线,小米的净化器应该是30和80两条线。

 四、数据更新

        对于前端显示,这里先提一下如何接收后端发来的数据的,如下图所示。以Connections对象为基础,设置它的属性target为theMainInterface,这个其实就是我们加载QML文件时候设置的前后端交互名称,这里用上了,相当于是信号发射者;信号接收器就是C++文件里定义的信号函数前面加个on,然后首字母改成大写就可以了,这里是s改为S,这样这里就能接收到后端发送过来的传感器数据了,很简单吧。至于如何显示,放到前端部分再讲解。

五、数据发送

        数据发送底层就是跟嵌入式端一样,组合后通过mqtt发送出去就行了,有点区别就是这时候要带上目标的序列号dev_sn,这样带有序列号的话题设备端才能收到数据。


void MainInterface::airSendLevel(u32 dev_sn, int cmd_type, u8 *cmd_buff, u16 cmd_len)
{
    u8 make_buff[500]={0};
    u16 make_len=0;
    
    make_buff[make_len++]=0xAA;
    make_buff[make_len++]=0x55;
    make_buff[make_len++]=0;
    make_buff[make_len++]=0;
    make_buff[make_len++]=dev_sn>>24;
    make_buff[make_len++]=dev_sn>>16;
    make_buff[make_len++]=dev_sn>>8;
    make_buff[make_len++]=dev_sn;
    make_buff[make_len++]=cmd_type;
    memcpy(&make_buff[make_len], cmd_buff, cmd_len);
    make_len+=cmd_len;
    make_buff[2]=make_len>>8;
    make_buff[3]=make_len;
    u16 crcValue=drv_com.crc16(make_buff, make_len);
    make_buff[make_len++]=crcValue>>8;
    make_buff[make_len++]=crcValue;
    QByteArray msg_ba((char*)make_buff, make_len);
    QString topic=QString::asprintf("air/dev/sub/%08X", dev_sn);
    if(m_mqttClient)
    {
        m_mqttClient->mqttPublishMessage(topic, msg_ba);
    }
}
六、指令下发

        在应用层,主要就是开关和调速两个功能,这里要看下这两个函数的定义,比较特别,在头文件定义的函数名称前多了Q_INVOKABLE,添加了这个关键字后,这个函数就可以在QML文件里直接调用了,是不是很方便。置于函数的内容应该比较简单了,就是组合下报文给底层函数发送出去就行了,这里的命令定义跟嵌入式端是一样的,两边有改动的话一定要及时同步,不然就乱了。对于调速设置,函数的输入是0~1的浮点数,当小于0.1的时候会强制等于0.1,这样调速最低时才不会停掉,只是慢速转动。

        对于这里的后端,总的来说没什么难点,主要还是做好前后端数据交互的准备。这里有个细节提一下,细心的也会发现,有个定时器槽函数slotCheckTimeout(),虽然这里没什么用,但是在其他有复杂任务或者多线程的时候很有用,它可以定时执行任务,相当于局部的main函数了,以后有机会再慢慢学习。

本项目的交流QQ群:701889554

   写于2024-4-2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/511632.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Springboot集成knife4j (swagger)

1、添加依赖 在pom.xml 文件中添加 knife4j-spring-boot-starter 的依赖 <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </depe…

TCP、UDP协议

TCP与UDP协议的区别 TCP&#xff08;Transmission Control Protocol&#xff09;和UDP&#xff08;User Datagram Protocol&#xff09;是两种常用的传输层协议&#xff0c;它们之间有以下几点区别&#xff1a; 1. 连接性&#xff1a; - TCP是面向连接的协议&#xff0c;通…

玩转ChatGPT:Kimi测评(科研写作)

一、写在前面 ChatGPT作为一款领先的语言模型&#xff0c;其强大的语言理解和生成能力&#xff0c;让无数用户惊叹不已。然而&#xff0c;使用的高门槛往往让国内普通用户望而却步。 最近&#xff0c;一款由月之暗面科技有限公司开发的智能助手——Kimi&#xff0c;很火爆哦。…

VMware-16.0配置虚拟机网络模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、为什么要配置网络&#xff1f;二、配置步骤1.检查VMware服务2.进入配置页面3.添加网络模式1.Bridge2.NAT3.Host-only 4.DHCP租约5.静态IP 三、使用总结 前言…

wife_wife【web 攻防世界】

大佬的wp:WEB&#xff1a;Wife_wife-CSDN博客 知识点&#xff1a; prototype是new class 的一个属性&#xff0c;即__proto__指向new class 的prototype属性__proto__如果作为json代码解析的话会被当成键名处理&#xff0c;但是如果是在类中的话则会被当成子类的原型 如let o…

OpenCV 4.9基本绘图

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV使用通用内部函数对代码进行矢量化 下一篇&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; ​目标 在本教程中&#xff0c;您将学习如何&am…

如何对Webpack进行优化

目录 1.优化-提取css代码 1.1. 插件 mini-css-extract-plugin 1.2. 步骤&#xff1a; 1.3. 注意 1.4. 好处 1.5. 练习 2. 优化-css代码提取后压缩 2.1. 问题引入 2.2. 解决 2.3. 步骤 3. Webpack打包less代码 3.1. 加载器 less-loader 3.2. 步骤 3.3. 注意&#xf…

【Redis 知识储备】应⽤数据分离架构 -- 分布系统的演进(2)

应⽤数据分离架构 随着系统的上线&#xff0c;我们不出意外地获得了成功。市场上出现了⼀批忠实于我们的⽤⼾&#xff0c;使得系统的访问量逐步上升&#xff0c;逐渐逼近了硬件资源的极限&#xff0c;同时团队也在此期间积累了对业务流程的⼀批经验。⾯对当前的性能压⼒&#x…

Android Studio学习8——点击事件

在xml代码中绑定 在java代码中绑定 弹出一个toast 随机&#xff0c;数组

基于Docker for Windows部署ChatGPT-Next-Web

基于Docker for Windows部署ChatGPT-Next-Web 项目地址安装Docker for Windows部署项目参数讲解参数示例 运行 项目地址 https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 安装Docker for Windows 官网地址&#xff1a;https://www.docker.com/ 下拉找到Download 选择W…

篮球竞赛预约平台的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)篮球馆,篮球赛,竞赛项目,赛事预约

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读300套最新项目持续更新中..... 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含ja…

数据库系统概论(超详解!!!) 第三节 关系数据库标准语言SQL(Ⅳ)

1.集合查询 集合操作的种类 并操作UNION 交操作INTERSECT 差操作EXCEPT 参加集合操作的各查询结果的列数必须相同;对应项的数据类型也必须相同 查询计算机科学系的学生及年龄不大于19岁的学生。SELECT *FROM StudentWHERE Sdept CSUNIONSELECT *FROM StudentWHERE Sage&l…

go之web框架gin

介绍 Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API&#xff0c;性能要好得多&#xff0c;多亏了 httprouter&#xff0c;速度提高了 40 倍。 如果您需要性能和良好的生产力&#xff0c;您一定会喜欢 Gin。 安装 go get -u github.com/gin-gonic/g…

SpringBoot | Spring Boot“整合Redis“

目录: 1. Redis 介绍2. Redis 下载安装3. Redis “服务开启”和“连接配置”4. Spring Boot整合Redis的“前期准备” :① 编写实体类② 编写Repository 接口③ 在“全局配置文件”中添加 “Redis数据库” 的 “相关配置信息” 5. Spring Boot整合“Redis” (案例展示) 作者简介…

Golang | Leetcode Golang题解之第5题最长回文子串

题目&#xff1a; 题解&#xff1a; func longestPalindrome(s string) string {if s "" {return ""}start, end : 0, 0for i : 0; i < len(s); i {left1, right1 : expandAroundCenter(s, i, i)left2, right2 : expandAroundCenter(s, i, i 1)if ri…

使用fusesource的mqtt-client-1.7-uber.jar,mqtt发布消息出去,接收端看到的是中文乱码,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

安全架构设计理论与实践相关知识总结

一、安全架构概述 常见信息威胁介绍&#xff1a; 1. 信息泄露&#xff1a;信息被泄露或透露给某个非授权实体 2. 破坏信息完整性&#xff1a;数据被非授权地进行增删改查货破坏而受到损失 3. 拒绝服务&#xff1a;对信息会其他资源的合法访问被无条件的组织 4. 非法使用&#x…

Linux网络编程二(TCP图解三次握手及四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)

文章目录 1、TCP三次握手(1) 第一次握手(2) 第二次握手(3) 第三次握手 2、TCP四次挥手(1) 一次挥手(2) 二次挥手(3) 三次挥手(4) 四次挥手 3、TCP滑动窗口4、TCP状态时序图5、多进程并发服务器6、多线程并发服务器 1、TCP三次握手 TCP三次握手(TCP three-way handshake)是TCP协…

【单】Unity _RPG项目中的问题

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a; ⭐…

数字乡村创新之路:科技引领农村实现高质量发展

随着信息技术的快速发展&#xff0c;数字乡村建设已成为推动农村高质量发展的重要引擎。数字乡村通过科技创新&#xff0c;不仅改变了传统农业生产方式&#xff0c;也提升了乡村治理水平&#xff0c;为农民带来了更加便捷的生活。本文将从数字乡村的内涵、科技引领农村高质量发…