• [技术干货] Linux系统下串口AT指令控制EC20连接华为云物联网平台
    一、前言在当今万物互联的时代背景下,物联网技术的快速发展极大地推动了智能化社会的构建。作为其中的关键一环,设备与云端平台之间的通信变得尤为重要。本文介绍如何在Linux操作系统环境下,利用串口通信来实现EC20模块与华为云物联网平台的有效连接。使用Linux下的/dev/ttyUSB0设备文件通过AT指令来配置EC20模块,采用C语言和Python这两种编程语言实现这一过程。EC20 是Quectel 生产的 4G LTE 模块。是一个多功能的通信模块,广泛应用于各种 IoT(物联网)设备中,提供了可靠的无线通信能力。EC20 支持 LTE FDD、LTE TDD、WCDMA 和 GSM 网络,能够实现高速的数据传输和稳定的语音通信。EC20 模块支持全球主要的频段,使其能够在多个国家和地区的网络环境中正常工作。它能够提供最高达 150 Mbps 的下行速率和 50 Mbps 的上行速率,这使得它非常适合需要高带宽的应用场景,如视频流、远程监控和数据传输。该模块具有强大的数据通信能力,包括支持 TCP/IP 和 UDP 协议的网络连接。EC20 还支持各种 AT 命令,通过这些命令用户可以控制模块的操作,配置网络设置,发送和接收数据。其内置的网络协议栈使得模块可以直接进行 MQTT、HTTP、FTP 等网络协议的通信,为开发者提供了极大的便利。除了数据通信功能,EC20 模块还支持语音通话和短信功能。这使得它不仅可以用于数据传输,还可以作为语音通信解决方案。通过 AT 命令,用户可以方便地进行拨打和接听电话,发送和接收短信,满足不同应用场景的需求。在电源管理方面,EC20 模块具有低功耗模式,能够有效地延长设备的电池寿命。它支持多种电源管理功能,包括睡眠模式和省电模式,使得它在不使用的时候能够降低功耗,减少能量消耗。模块的物理接口包括多个 UART 串口、USB 接口和 GPIO 引脚,这些接口允许模块与其他硬件进行连接。通过这些接口,用户可以实现串口通信、USB 数据传输以及各种数字信号的输入输出,提供了高度的灵活性和可扩展性。在设计和生产方面,EC20 模块遵循了工业标准,确保其在各种恶劣环境下的可靠性。它的设计小巧且坚固,适合嵌入到各种嵌入式系统和终端设备中。Quectel 提供了详细的技术文档和开发工具,帮助开发者快速集成和部署模块。二、实例代码2.1 服务器信息下面是我的华为云物联网服务器设备信息。关于创建过程,可以看视频:cid:link_0IP地址:117.78.5.125端口号:1883ClientId 64000697352830580e48df07_dev1_0_0_2023030206Username 64000697352830580e48df07_dev1Password a695af9883c5d0e3817bc6971beeecadf8c7c595677c461b1fe75882ed2bf449订阅主题:$oc/devices/64000697352830580e48df07_dev1/sys/messages/down发布主题:$oc/devices/64000697352830580e48df07_dev1/sys/properties/report发布的消息:{"services": [{"service_id": "stm32","properties":{"DHT11_T":18,"DHT11_H":80,"MQ2":1,"water":1,"flame":1,"light":0,"LED1":0,"LED2":0,"LED3":0}}]}2.2 Python代码本小节介绍 通过串口 /dev/ttyUSB0 发送 AT 指令以控制 EC20 模块连接华为云物联网平台,并完成 MQTT 通信,使用 Python 脚本实现。AT 指令:AT+RST: 重启模块AT+CSIM=1: 设置工作模式为数据模式(请根据实际模块手册确认是否需要此指令)AT+QMTOPEN=0,"IP地址",端口号: 连接到 MQTT 服务器AT+QMTCONN=0,"ClientId","Username","Password": 使用提供的 ID、用户名和密码连接到 MQTT 服务器AT+QMTSUB=0,0,"主题",1: 订阅指定主题AT+QMTPUB=0,0,0,0,"主题",消息内容: 发布消息到指定主题下面是实现代码,展示了如何用 Python 通过串口发送 AT 指令来配置 EC20 模块并连接到华为云物联网平台:# -*- coding: utf-8 -*-​import serialimport time​# 串口配置SERIAL_PORT = '/dev/ttyUSB0'BAUDRATE = 115200TIMEOUT = 1​# MQTT 服务器信息MQTT_SERVER_IP = '117.78.5.125'MQTT_SERVER_PORT = '1883'MQTT_CLIENT_ID = '64000697352830580e48df07_dev1_0_0_2023030206'MQTT_USERNAME = '64000697352830580e48df07_dev1'MQTT_PASSWORD = 'a695af9883c5d0e3817bc6971beeecadf8c7c595677c461b1fe75882ed2bf449'​# 发布的消息PUBLISH_MESSAGE = '{"services": [{"service_id": "stm32","properties":{"DHT11_T":18,"DHT11_H":80,"MQ2":1,"water":1,"flame":1,"light":0,"LED1":0,"LED2":0,"LED3":0}}]}'​# 发送 AT 指令并获取响应def send_at_command(serial_conn, command, response_termination='OK', delay=1): print(f"Sending command: {command}") serial_conn.write((command + '\r\n').encode()) time.sleep(delay) response = serial_conn.read(serial_conn.inWaiting()).decode() print(f"Response: {response}") if response_termination and response_termination not in response: raise Exception(f"Unexpected response: {response}") return response​def main(): # 打开串口连接 with serial.Serial(SERIAL_PORT, BAUDRATE, timeout=TIMEOUT) as ser: try: # 重启模块 send_at_command(ser, 'AT+RST')​ # 设置工作模式为数据模式 send_at_command(ser, 'AT+CSIM=1')​ # 连接到 MQTT 服务器 send_at_command(ser, f'AT+QMTOPEN=0,"{MQTT_SERVER_IP}",{MQTT_SERVER_PORT}') send_at_command(ser, 'AT+QMTCONN=0,"{MQTT_CLIENT_ID}","{MQTT_USERNAME}","{MQTT_PASSWORD}"') time.sleep(5) # 等待连接​ # 订阅主题 send_at_command(ser, 'AT+QMTSUB=0,0,"$oc/devices/64000697352830580e48df07_dev1/sys/messages/down",1')​ # 发布消息 send_at_command(ser, f'AT+QMTPUB=0,0,0,0,"$oc/devices/64000697352830580e48df07_dev1/sys/properties/report",{PUBLISH_MESSAGE}')​ except Exception as e: print(f"An error occurred: {e}")​if __name__ == "__main__": main()运行命令:root@flexusx-1a58:~# python3 mqtt_connect.py 2.3 C语言代码本小节介绍 用 C 语言实现通过串口发送 AT 指令来控制 EC20 模块并完成 MQTT 通信。下面是实现代码,通过串口配置 EC20 模块并连接到 MQTT 服务器:#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <termios.h>#include <errno.h>​#define SERIAL_PORT "/dev/ttyUSB0"#define BAUDRATE B115200​int setup_serial_port(const char *port) { int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); }​ struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, BAUDRATE); cfsetospeed(&options, BAUDRATE);​ options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8;​ tcsetattr(fd, TCSANOW, &options);​ return fd;}​void send_at_command(int fd, const char *command) { write(fd, command, strlen(command)); write(fd, "\r\n", 2); usleep(100000); // Wait for 100ms for the module to respond​ char response[256]; int n = read(fd, response, sizeof(response) - 1); if (n > 0) { response[n] = '\0'; printf("Response: %s\n", response); } else { printf("No response or read error.\n"); }}​int main() { int fd = setup_serial_port(SERIAL_PORT);​ // 1. Reset the module send_at_command(fd, "AT+RST");​ // 2. Connect to MQTT server char command[300]; snprintf(command, sizeof(command), "AT+QMTOPEN=0,\"%s\",%d", "117.78.5.125", 1883); send_at_command(fd, command);​ snprintf(command, sizeof(command), "AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"", "64000697352830580e48df07_dev1_0_0_2023030206", "64000697352830580e48df07_dev1", "a695af9883c5d0e3817bc6971beeecadf8c7c595677c461b1fe75882ed2bf449"); send_at_command(fd, command);​ usleep(5000000); // Wait 5 seconds for connection to establish​ // 3. Subscribe to a topic send_at_command(fd, "AT+QMTSUB=0,0,\"$oc/devices/64000697352830580e48df07_dev1/sys/messages/down\",1");​ // 4. Publish a message sprintf(command, "AT+QMTPUB=0,0,0,0,\"$oc/devices/64000697352830580e48df07_dev1/sys/properties/report\",\"{\\\"services\\\": [{\\\"service_id\\\": \\\"stm32\\\",\\\"properties\\\":{\\\"DHT11_T\\\":18,\\\"DHT11_H\\\":80,\\\"MQ2\\\":1,\\\"water\\\":1,\\\"flame\\\":1,\\\"light\\\":0,\\\"LED1\\\":0,\\\"LED2\\\":0,\\\"LED3\\\":0}}]}\""); send_at_command(fd, command);​ close(fd); return 0;}编译运行:root@flexusx-1a58:~# gcc mqtt_connect.c root@flexusx-1a58:~# ./a.out 2.4 创建虚拟串口方便测试在与硬件通信之前,可以先建立一个虚拟串口测试一下。创建一个虚拟的串口设备 /dev/ttyUSB0 主要有两种方式:使用 socat 工具创建虚拟串口对(虚拟串口设备),或者使用 tty0tty 驱动来创建虚拟串口对。方法 1: 使用 socat 创建虚拟串口socat 是一个强大的网络工具,可以创建虚拟串口设备。下面是如何使用 socat 创建两个虚拟串口对:安装 socat: 在大多数 Linux 发行版上,可以使用包管理工具安装 socat。例如,在 Debian 或 Ubuntu 上运行:sudo apt-get install socat创建虚拟串口对: 运行以下命令来创建两个虚拟串口设备 /dev/ttyV0 和 /dev/ttyV1:socat -d -d PTY,link=/dev/ttyV0,raw,echo=0 PTY,link=/dev/ttyV1,raw,echo=0这将创建两个虚拟串口设备 /dev/ttyV0 和 /dev/ttyV1,它们会像物理串口一样工作,可以在程序中使用它们进行测试。使用虚拟串口设备:可以将一个设备用于发送数据,另一个设备用于接收数据进行测试。例如,可以将一个串口设备配置为连接到模拟的设备,另一个设备配置为模拟的串口设备的接收端。方法 2: 使用 tty0tty 驱动tty0tty 是一个内核模块,用于创建虚拟串口对。以下是如何安装和使用 tty0tty:安装 tty0tty: 需要从源代码编译和安装 tty0tty。按照以下步骤进行:sudo apt-get install build-essential linux-headers-$(uname -r)git clone https://github.com/ntop/tty0tty.gitcd tty0ttymakesudo make installsudo depmod -a加载内核模块: 运行以下命令加载 tty0tty 内核模块:sudo modprobe tty0tty这将创建虚拟串口设备 /dev/ttyt0 和 /dev/ttyt1。使用虚拟串口设备:/dev/ttyt0 和 /dev/ttyt1 现在可以作为虚拟串口设备使用。可以在程序中将它们作为串口设备来进行测试。示例代码假设已经创建了 /dev/ttyV0 和 /dev/ttyV1(使用 socat),可以在 Python 中使用这些虚拟串口设备进行测试。例如:import serial​# 打开虚拟串口ser1 = serial.Serial('/dev/ttyV0', 115200, timeout=1)ser2 = serial.Serial('/dev/ttyV1', 115200, timeout=1)​# 发送数据ser1.write(b'Hello, world!\n')​# 读取数据response = ser2.readline()print('Received:', response.decode('utf-8'))​# 关闭串口ser1.close()ser2.close()
  • [专题汇总] IOT物联网,七月月份问题汇总来了。
    1. 设备发送字符串到Iot,一直显示不想要的,而且属性也没更新cid:link_0  2.消息跟踪导出的压缩包解压失败cid:link_1  3. 按照API Explore里面的示例代码,require为什么报错,怎么修改,求各位大佬解惑cid:link_9  4.定义了物理模型但是不出现对应属性 cid:link_2   5.新新新新新新手 请问获取到了token,怎么使用这token请求调用api查询影子数据啊,调用的api是调试那里的真实请求url那里的吗  cid:link_3   6.修改设备属性API请求超时 cid:link_10   7. IOTDA.001006 cid:link_11  设备上报多个属性失败 cid:link_4   9. 数据转发到设备的topic定义  cid:link_5   10.真机调试未激活,串口工具报错 cid:link_6   11. 使用API查询设备属性或设备影子数据,是否支持同时查询多个设备 cid:link_7   12. 应用端请求为什么不行了cid:link_12   13.使用Golang开发一个简易版shell【转】 cid:link_13   14.python导入其它py文件的实现步骤【转】 cid:link_14   15.Python property函数的具体使用【转】 cid:link_15   16.正则表达式的神奇世界之表达、匹配和提取全解析【转】 cid:link_8   17.正则去除中括号(符号)及里面包含的内容(最新推荐)【转】 cid:link_16   18.Javaweb项目启动Tomcat常见的报错解决方案【转】 cid:link_17   19.SpringBoot+Vue前后端分离实现审核功能的示例【转】 cid:link_18   20.Java中Collections.sort()排序方法举例详解【转】 cid:link_19   21.Java中回调函数 (callback) 及其实际应用场景【转】 cid:link_20 
  • [技术干货] docker国内镜像源配置及走代理设置-转载
    配置docker国内镜像源/代理 配置docker国内镜像源 docker命令走代理 同步发布在个人笔记docker国内镜像源配置及走代理设置  配置docker国内镜像源 使用docker拉取镜像的时候,有时候会卡在中途死活下载不完。一般这种情况就是因为docker默认的镜像源是Dockerhub,而这个网站在域外。所以和Ubuntu换国内源一样,我们也可以对docker换国内镜像源。 docker的镜像源文件配置在 /etc/docker/daemon.json处,如果没有的话我们就创建一个然后再修改。  sudo vim  /etc/docker/daemon.json 常见的国内源有:  Docker国内镜像源:https://registry.docker-cn.com 中科大源:https://docker.mirrors.ustc.edu.cn 网易源:https://hub-mirror.c.163.com 百度源:https://mirror.baidubce.com 腾讯源:https://ccr.ccs.tencentyun.com 阿里源:需要登陆cr.console.aliyun.com创建单独的镜像源链接,就不介绍了。 根据测试情况,酌情在配置文件中添加需要的镜像源链接,如下所示:  {     "registry-mirrors": [         "https://registry.docker-cn.com",         "https://docker.mirrors.ustc.edu.cn",         "https://hub-mirror.c.163.com",         "https://mirror.baidubce.com",         "https://ccr.ccs.tencentyun.com"     ] } 重启docker,注意由于走的是守护程序daemon,所以daemon进程也需要重启。  sudo systemctl daemon-reload        #重启daemon进程 sudo systemctl restart docker        #重启docker 最后我们再验证一下是否修改成功,运行  docker info 在长串info信息中如果出现类似下文的内容:   Registry Mirrors:   https://docker.mirrors.ustc.edu.cn/   http://hub-mirror.c.163.com/   https://mirror.ccs.tencentyun.com/   https://registry.docker-cn.com/ 那就说明我们的docker国内镜像源修改成功了。  docker命令走代理 然而实际测试下来,就算我们修改成功了国内的镜像源,有时候由于国内镜像更新不及时,或者需要拉取的镜像比较冷门,只有域外镜像站才有,那么我们不得不让docker pull命令,走我们的代理。 我们在docker的进程服务文件夹配置我们的代理设置,如果没有我们就新建这个文件夹:  sudo mkdir /etc/systemd/system/docker.service.d 1 然后在docker.service.d文件夹里新建我们的代理文件proxy.conf  sudo vim proxy.conf 1 并把文件写如下面这个格式:  Environment="HT*P_PROXY=代理服务器ip:port"  Environment="HT*PS_PROXY=代理服务器ip:port" 假如我们本机已经设置好代理了,那么代理服务器就可以写为localhost,端口就是我们设置的http和https代理端口即可,形如:  [Service]  Environment="H*TP_PROXY=localhost:port"  Environment="HT*PS_PROXY=localhost:port" 保存并退出proxy.conf文件,和更改镜像源一样,重启docker,并重启daemon进程。  sudo systemctl daemon-reload        #重启daemon进程 sudo systemctl restart docker        #重启docker 最后我们仍然是验证一下是否修改成功,运行  docker info 在长串info信息中如果出现类似下文的内容:   HTTP Proxy: 代理服务器ip:port  HTTPS Proxy: 代理服务器ip:port 那就说明我们已经成功设置docker pull命令走代理了,一般情况下也就不会出现拉取镜像卡死的情况了。 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Lichen0196/article/details/137355517 
  • [技术干货] 【Linux】使用 iptables 验证访问HDFS 所使用到的端口
    一、实操背景 背景: 在客户有外网的服务器需要访问内网大数据集群HDFS,使用iptable模拟测试需要开放的端口。  二、iptables 简介 具体介绍看文章: 【Linux】Iptables 详解与实战案例_linux iptable-CSDN博客  表(tables)提供特定的功能,iptables内置了4个表,即filter表、nat表、mangle表和raw表,分别用于实现包过滤,网络地址转换、包重构(修改)和数据跟踪处理。  链(chains)是数据包传播的路径,每一条链其实就是众多规则中的一个检查清单,每一条链中可以有一 条或数条规则。当一个数据包到达一个链时,iptables就会从链中第一条规则开始检查,看该数据包是否满足规则所定义的条件。如果满足,系统就会根据 该条规则所定义的方法处理该数据包;否则iptables将继续检查下一条规则,如果该数据包不符合链中任一条规则,iptables就会根据该链预先定 义的默认策略来处理数据包。  规则链名包括(也被称为五个钩子函数(hook functions)):  INPUT链 :处理输入数据包。 OUTPUT链 :处理输出数据包。 FORWARD链 :处理转发数据包。 PREROUTING链 :用于目标地址转换(DNAT)。 POSTOUTING链 :用于源地址转换(SNAT)。  三、模拟操作 对某台HDFS Client服务器禁止所有的INPUT FORWARD OUTPUT 只对某些端口开放。  iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT DROP 开启 22端口 可以使用远程工具连接  iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT  开启8020 ,88, 749 端口  iptables -I OUTPUT -s 192.168.2.155 -p tcp -d 192.168.2.154 --dport 8020 -j ACCEPT   iptables -I OUTPUT -s 192.168.2.155 -p udp -d 192.168.2.152 --dport 88 -j ACCEPT iptables -I OUTPUT -s 192.168.2.155 -p udp -d 192.168.2.152 --dport 749 -j ACCEPT  上传文件报错 datanode 1019端口  继续放开 DataNode 1019端口  iptables -I OUTPUT -s 192.168.2.155 -p tcp -d 192.168.2.152 --dport 1019 -j ACCEPT  iptables -I OUTPUT -s 192.168.2.155 -p tcp -d 192.168.2.153 --dport 1019 -j ACCEPT  iptables -I OUTPUT -s 192.168.2.155 -p tcp -d 192.168.2.154 --dport 1019 -j ACCEPT  iptables -I OUTPUT -s 192.168.2.155 -p tcp -d 192.168.2.155 --dport 1019 -j ACCEPT  文件成功上传到 HDFS  至此我们可以看到客户端访问HDFS使用到的端口如下:  端口  作用  8020  NameNode元数据访问  88  Kerberos Server  749  Kerberos admin  1019  DataNode访问  端口限制参考:centos 利用iptables来配置linux禁止所有端口登陆和开放指定端口的方法_centos如何禁止所有网络访问只开放某些ip-CSDN博客 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/qq_35995514/article/details/139700591 
  • [技术干货] 【C++/STL】:list容器的深度剖析及模拟实现-转载
    🚀前言 点击跳转到文章:【list的基本使用】 要模拟实现list,必须要熟悉list的底层结构以及其接口的含义,list的底层是带头双向循环链表,通过上一篇文章的学习,这些内容已基本掌握,现在我们来模拟实现list容器的主要接口。  与前面的vector类似,由于使用了模板,也只分成.cpp和.h两个文件。  .cpp文件里放节点类,迭代器类,list类及其成员函数,测试函数的实现,在.h文件里进行测试。  本文的重点是:对三个类的区分与理解,迭代器类的实现。  🚀一,节点类 1.为什么定义节点结构体时使用struct而不是class?  答:(1)其实用class也可以,但是class与struct默认的访问限定不同,当没有声明公有,私有时,struct内容默认是公有,class内容默认的私有,所以用class要加上public。  (2)当我们用class没有加上public,也没有实例化对象时,编译不会报错(报私有成员的错误),因为模版是不会被细节编译的。只有当我们实例化出对象,模版才会被编译,并且类的实例化并不是对所有成员函数都实例化,而是调用哪个成员函数就实例化哪个。这叫做按需实例化。  2.可用匿名对象初始化。如果T是自定义类型,则调用其默认构造,并且T是内置类型也升级成了有默认构造的概念了。  template <class T> struct ListNode {     ListNode<T>* _next;     ListNode<T>* _prev;      T _data;      ListNode(const T& data = T())         :_next(nullptr)         ,_prev(nullptr)         ,_data(data)     {} }; 🚀二,迭代器类 前面学习的string类和vector的迭代器用的是原生指针类型,即T*。但是在list容器中是不能这样的,因为前面两者的底层物理空间是连续的,符合迭代器++与- -的行为。但是list是由一个一个节点构成的,物理空间不连续,Node*的++和- -不符合迭代器的行为,无法变遍历。  所以用一个类把Node* 封装,就可以重载运算符,使得用起来像内置类型,但会转换成函数调用,继而控制Node*的行为。  1,普通迭代器类的实现 遍历需要的核心运算符重载是 *,!=,++ 和 ->。所以只需要利用带头双向循环链表的特性,对Node * 进行封装,从而控制Node * 的行为。  class ListIterator {     typedef ListNode<T> Node;     typedef ListIterator<T> Self;//名字变得简短  public:     Node* _node;//定义一个节点指针      ListIterator(Node* node)         :_node(node)     {}      //前置:返回之后的值     //++it;//返回与自己一样的类型     Self& operator++()     {         _node = _node->_next;         return *this;     }      Self& operator--()     {         _node = _node->_prev;         return *this;     }      //后置:返回之前的值     Self operator++(int)     {         Self tmp(*this);         _node = _node->_next;         return tmp;     }      Self operator--(int)     {         Self tmp(*this);         _node = _node->_prev;         return tmp;     }      T& operator*()     {         return _node->_data;     }          //返回的是数据的地址     T* operator->()     {         return &_node->_data;     }      bool operator!=(const Self& it)     {         return _node != it._node;     }      bool operator==(const Self& it)     {         return _node == it._node;     } }; 2,->运算符的使用场景 假设某个场景下存在一个坐标类:  struct Pos {     int _row;     int _col;      Pos(int row = 0,int col = 0)         :_row(row)         ,_col(col)     {} }; 如果我们插入坐标,并且想要打印出坐标,该如何遍历?  错误示范:  void test_list2() {     list<Pos> lt1;     lt1.push_back(Pos(100, 100));//使用匿名对象     lt1.push_back(Pos(200, 200));     lt1.push_back(Pos(300, 300));          //这里的it是Pos*,是结构体指针     list<Pos>::iterator it = lt1.begin();     while (it != lt1.end())     {         cout << *it << " ";//err         ++it;     }     cout << endl; } 原因:因为这里的*it返回的是Pos自定义类型,而访问自定义类型需要需要在类中自己重载流插入(<<),这里并没有重载,所以报错。  正确操遍历的两种方式:  方式1:通过.操作符直接访问结构体的成员变量(一般不这样访问数据)。  cout << (*it)._row << ":" << (*it)._col << endl;//ok 1 方式2:通过重载->运算符,对结构体指针进行解引用。  cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;//ok 1 注意:其实这里严格来说是有两个箭头,第一个运算符重载的调用 it.operator->() 返回的是 Pos*,第二个箭头才是原生指针,Pos*再用箭头访问。为了可读性,省略了一个->。  void test_list2() {     list<Pos> lt1;     lt1.push_back(Pos(100, 100));//使用匿名对象     lt1.push_back(Pos(200, 200));     lt1.push_back(Pos(300, 300));          //这里的it是Pos*,是结构体指针     list<Pos>::iterator it = lt1.begin();     while (it != lt1.end())     {             //方式1:         //cout << (*it)._row << ":" << (*it)._col << endl;//ok         //*it就是Pos结构体,再用.操作符访问成员                  //方式2:         cout << it->_row << ":" << it->_col << endl;//ok         //cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;//ok                  ++it;     }     cout << endl; } 3,const迭代器类的实现 在我们遍历数据时,有时会写一个打印函数,引用传参,一般建议加const,这就出现了一个const链表:  void Func(const list<int>& lt1) {     list<int>::const_iterator it = lt1.begin();     while (it != lt1.end())     {         cout << *it << " ";         ++it;     }     cout << endl; } const迭代器不是在普通迭代器前面加const,即不是const iterator。  //err 这样使it本身也不能++了 const list< int >::iterator it = it.begin(); const 迭代器目的:本身可以修改,指向的内容不能修改,类似const T* p。  所以我们要再定义一个类,控制*和->的返回值就可以了。  template <class T> class ListConstIterator {     typedef ListNode<T> Node;     typedef ListConstIterator<T> Self;//名字变得简短  public:     Node* _node;//定义一个节点指针      ListConstIterator(Node* node)         :_node(node)     {}      //前置:返回之后的值     //++it;//返回与自己一样的类型     Self& operator++()     {         _node = _node->_next;         return *this;     }      Self& operator--()     {         _node = _node->_prev;         return *this;     }      //后置:返回之前的值     Self operator++(int)     {         Self tmp(*this);         _node = _node->_next;         return tmp;     }      Self operator--(int)     {         Self tmp(*this);         _node = _node->_prev;         return tmp;     }      // 所以我们要再定义一个类,使用const控制*和->的返回值就可以     const T& operator*()     {         return _node->_data;     }      const T* operator->()     {         return &_node->_data;     }      bool operator!=(const Self& it)     {         return _node != it._node;     }      bool operator==(const Self& it)     {         return _node == it._node;     } }; 4,通过模板参数,把两个类型的迭代器类结合 可以发现,其实普通迭代器和const迭代器的本质区别是 * 和 ->,这两个运算符的返回类型的变化。两个类冗余,所以可以通过模板,给不同的模板参数,让编译器自己实例化两个类。                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/2301_77900444/article/details/139688063 
  • [技术干货] Vscode——SSH连接不进去服务器的万能解决办法 -转载
    1、移动 单个文件: sudo mv 文件夹名/文件名 目标文件夹路径 例: sudo mv /home/csj/slambook/ch4/sophus /usr/include 多个文件: sudo mv 文件夹名/* 目标文件夹路径 例: 将 /usr/runoob 下的所有文件和目录移到/usr/target目录下 mv /usr/runoob/*  /usr/target  参考: Linux mv 命令 2、复制/重命名 sudo cp -r 文件夹名/文件名 目标文件夹路径  例: sudo cp -r /home/csj/slambook/ch4/sophus /usr/include 3、删除 sudo rm -r 文件夹名/文件名 例: sudo rm -r /home/csj/slambook/ch4/sophus 4、新建 sudo mkdir 文件夹名 例: sudo mkdir 存贮 5、运行shell 脚本文件 1、打开至该脚本的目录 2、然后输入: 格式: bash shell脚本文件名称 例: bash shell_name.sh 或者 bash shell_name 6、为软件创建菜单图标 Ubuntu——为软件创建菜单(桌面)图标的方法 7、查看ubuntu的版本 lsb_release -a 8、下载文件 wget:是一个下载文件的工具 格式: wget 需要下载的文件链接 例: wget https://motchallenge.net/data/MOT17.zip 9、查看文件大小 格式: ls -lh XXX 例(下载palace.mp4): ls -lh /mnt/A/pk_cs/Code/palace.mp4 参考: Linux查看文件大小的几种方法 10、查看文件夹大小 查看某一个文件夹 命令: 进入文件夹后,输入 du -sh 命令2: du -s 文件夹 例: 进入MOT17文件夹,输入du -sh命令,得到: 只列出当前目录下的文件夹大小 命令: du -sh ./*  查看当前目录下所有目录及其各个子目录的大小 命令: du -h . 11、查看所有磁盘的内存情况 命令:  df -h 12、解压文件 12.1 单个小压缩包解压 格式: unzip xxxxx.zip unzip file.zip -d destination_folder 例: unzip data.zip 参考: ubuntu下如何解压zip文件(超详细) 12.2 多个拆分压缩包解压 参考: Ubuntu——.z01 .z02 .z03此类拆分后的压缩文件解压办法 12.3 单个大压缩包解压(解压大文件报错的解决方案) 参考: Unzip——大文件解压报错:invalid compressed data to inflate file #14: bad zipfile offset (local header sig) ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/Williamcsj/article/details/121172012 
  • [技术干货] 【网络】网络基础(一)-转载
     一、计算机网络背景 1.1网络发展 在计算机发展初期,计算机之间相互独立,每个终端各自持有数据,这明显对于数据的共享很不友好,为了高效的数据传输和共享,世界各地的科学家、实验室就开始想办法提出自己的解决方案用来解决多设备数据共享的问题,所以在这一阶段形成了网络互联的概念,在这一阶段,不同设备之间可以进行数据共享,那么随着设备的增多,技术的发展,就产生了局域网,广域网这样的技术概念,那么具体的内容我们我们慢慢抽丝剥茧学习,这部分只是有一个网络发展的概念就行。  1.2认识“协议” 我们之前说为了解决多设备之间数据共享的问题,世界各地的实验室、科学家们提出了诸多解决方案,而这个数据通信的解决方案其实就是协议。  协议可以理解成一种约定,即通信双方提前约定好通信的数据格式,在通信过程中双方基于该数据格式进行通信,站在语言角度理解协议就是通信双方都能识别的结构体数据类型。  但是这时就存在一个问题,计算机的生产厂商有很多,操作系统也有很多,衍生的网络硬件设备也有很多,那么如何让这些不同厂商之间生产的计算机都能相互顺畅的通信呢,所以这时就需要标准的存在,而这个统一的标准就是网络协议。  当今的网络协议是OSI(Open System Interconnection)提出的OSI七层模型。  为什么网络协议要分层?  联想到我们之前对于分层的理解,分层一般是为了可以松耦合,层与层之间不会有太多的联系,方便后期维护。  比如:当物理层发生变化时,我们只需要改动物理层的代码,其他层次的代码无需改动。  为什么存在网络协议?  首先客观事实是,通信距离变远,必定会引发新的问题:  如何使用数据的问题; 可靠性问题; 主机定位问题; 数据报局域网转发问题。 那么以上这些问题我们都会在后面的学习中有所体现,简言之网络协议的不同层次可以分别解决上述的问题。  二、网络协议初识 2.1OSI七层模型 OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范; 把网络从逻辑上分为了7层,每一层都有相关、相对应的物理设备,比如路由器,交换机; OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输; 它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整. 通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯; 但是,它既复杂又不实用,所以我们按照TCP/IP四层模型来讲解。 分层名称    功能    每层功能概述 应用层    针对特定应用的协议     表示层    设备固有数据格式和网络标准数据格式的转换     会话层    通信管理、负责建立和断开通信连接(数据流动的逻辑通路)、管理传输层以下的分层     传输层(TCP)    管理两个节点之间的数据传输、负责可靠性传输(确保数据被可靠地传送到目标地址,即数据传输可靠性)     网络层(IP)    地址管理与路由选择(路由器)     数据链路层    互联设备之间传送和识别数据帧(交换机)     物理层    以0/1代表电压的高低以及灯光的闪灭、界定连接器和网线的规格(集线器、调制解调器)     为什么上面我们说是TCP/IP四层模型呢?  OSI七层模型是非常优秀的网络协议,但是对于应用层、表示层和会话层,一般这三个层次被统一称为应用层,原因大概是因为这三个层次如果分开实现会引发其他问题导致问题复杂,这三层一般是由用户实现。  对于物理层我们一般不关心,所以在软件的角度考量,我们认为是OSI四层模型就可以,或者带上物理层称为OSI五层模型。  2.2OSI五层模型 TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇。  TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。  物理层: 负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、早期以太网采用的的同轴电缆(现在主要用于有线电视)、光纤, 现在的wifi无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等. 集线器(Hub)工作在物理层。 数据链路层: 负责设备之间的数据帧的传送和识别。例如网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作。 有以太网、令牌环网,无线LAN等标准。交换机(Switch)工作在数据链路层。 网络层: 负责地址管理和路由选择。例如在IP协议中,通过IP地址来标识一台主机,并通过路由表的方式规划出两台主机之间的数据传输的线路(路由)。路由器(Router)工作在网路层。 传输层: 负责两台主机之间的数据传输。如传输控制协议 (TCP),能够确保数据可靠的从源主机发送到目标主机。 应用层: 负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。我们的网络编程主要就是针对应用层 数据链路层解决数据报局域网转发问题,该层一般集成在网卡驱动程序中实现。  网络层解决主机定位问题。  传输层解决可靠性问题。  应用层解决如何使用数据的问题。  网络层和传输层集成在OS内部实现。  即操作系统源码中包含有实现网络层(IP协议)和传输层(TCP协议)的代码。  这两个层次合起来也称为TCP/IP协议栈。  所有的操作系统只要入网,就必须遵守TCP/IP协议栈,保证不同主机之间的数据通信。  虽然操作系统千千万,但是操作系统中TCP/IP协议栈的内容一定是一样的,这也就是为什么不同设备间可以进行通信的缘由。  所以你知道了网络也是OS源码的一部分,所以应用层想要使用网络,就必须存在有系统调用接口,而将这些系统调用进行封装就形成了网络库。  三、网络传输基本流程 3.1局域网通信  场景:A设备给C设备发送数据。  实际上,B设备也受到了A设备的数据,但是B设备查看时发现不是发给自己的消息就忽略了,而C设备收到分析后发现是发送给自己的数据。  即局域网中所有主机都受到了消息,经过对比Mac地址后确定通信双方。  区分不同设备的依据是Mac地址,不同设备有不同的Mac地址。  MAC地址(Media Access Control Address),也称为物理地址或硬件地址,是一个用来确认网络设备位置的地址。在OSI模型中,MAC地址位于数据链路层,它使得数据可以从源网络介质发送至目标网络介质,从而进行可靠的数据传输。  MAC地址具有以下特点:  唯一性:MAC地址是全球唯一的,每个网络设备(如网卡、路由器、交换机等)都有一个唯一的MAC地址。 固定性:MAC地址是写在网卡上的,一般来说是固化在网卡上的EEPROM(电可擦写可编程只读存储器)中,可通过网卡驱动程序进行配置。 层次化:MAC地址由六组两个十六进制数(共六个字节)组成,用冒号(:)或者连字符(-)分隔。前三个字节称为组织唯一标识符(OUI),用来标识厂商或组织;后三个字节由厂商自行分配,标识网络设备。 在发送数据和接收数据的过程中,可能会发生数据碰撞(一个局域网就是一个碰撞域),这是因为局域网可以被视为一种临界资源,在任何一个时刻,仅允许一个设备向局域网中发送数据。  如果数据链路层是以太网的标准,那么以太网通信标准中(碰撞检测与避免),当设备A发送数据发生数据碰撞后,设备A就停止发送,让出局域网这个临界资源供其他设备使用,其他设备皆是如此,所以以太网的处理方式可以认为是一种乐观的处理机制,因为同一时间有两个设备同时发送数据的概率并不高。 如果数据链路层是令牌环网的标准,那么局域网中会存在一个令牌,谁持有令牌谁可以发送数据,有点类似持有锁。 所以思考下:局域网中,主机越多越好还是越少越好?  当然是越少越好,越少就意味着更小的数据碰撞可能性,当主机数目变多后,会存在有交换机这样的设备来解决处理。  如果要黑掉一个局域网,就不断地向该局域网中发送垃圾数据🤐。  3.2网络传输流程 不跨子网的网络传输  当设备A向设备B发送数据时,报文并不是直接发送过去的,而是需要自顶向下进行封装,即添加每一层的协议报头,然后到达链路层后将封装后的数据报文发送给以太网。  报文=协议报头(快递单)+有效载荷(快递)。  报文不断被自顶向下进行交付的过程,需要添加每一层的协议报头,叫做封装。  而当报文经过以太网传输到设备B时,需要解析报文,解析报文需要两个步骤:  将报头和有效载荷分离; 判断将有效载荷交付给上层的哪一个协议(每层可能存在有不同的协议,比如传输层可能存在有TCP协议和UDP协议)。 这两个步骤被称为:解包与分用。  协议报头中包含了类似报头有多长,载荷有多长,上层协议是什么等信息。  通过上述数据传输流程,逻辑上:同层协议都可以认为自己在和对方直接通信。  不同协议层对数据包有不同的称谓:  应用层:request/response; 传输层:数据段; 网络层:数据报; 链路层:数据帧。 跨子网的网络传输  当网络传输跨子网时,我们需要路由器来进行转发。  路由器一般至少要横跨两个子网,这句话表明路由器要有两张网卡,有两个mac地址。  报文一般会存在有两套地址:  src(源IP地址)、dst(目的IP地址); 上一站从哪来(源mac地址)、下一站到哪去(目标mac地址)。 很明显第一套地址基本是不变的,因为数据传输一定有明确的通信双方,这个不变的地址也是路径规划的依据。  存在一些特殊情况,比如在数据传输过程中使用NET技术,其源IP地址会发生变化,但至少目的IP地址是不会变化的。  而第二套地址即mac地址在跨子网的情况下是会变化的,当数据跨网络到达另一个局域网时,其源mac地址和目的mac地址就需要发生变化,因此当数据达到路由器时,路由器会将该数据当中链路层的报头去掉**(解包),然后再重新封装一个报头(重新封装)**,此时该数据的源mac地址和目的mac地址就发生了变化。  而这个过程最重要的是:通过IP协议,我们能够在不同子网间进行通信,无需关注底层的MAC地址变化。IP地址是网络通信的关键,它确保了数据能够准确地从源地址传输到目的地址。  3.3网络中的地址管理 IP地址 IP协议有两个版本,IPv4和IPv6。 IP地址是在IP协议中,用来标识网络中不同主机的地址;对于IPv4来说,IP地址是一个4字节,32位的整数; 我们通常也使用"点分十进制"的字符串表示IP地址,例如192.168.0.1。用点分割的每一个数字表示一个字节,范围是0 - 255; MAC地址 MAC地址用来识别数据链路层中相连的节点; 长度为48位,及6个字节。一般用16进制数字加上冒号的形式来表示(例如: 08:00:27:03:fb:19) 在网卡出厂时就确定了,不能修改。mac地址通常是唯一的(虚拟机中的mac地址不是真实的mac地址,可能会冲突;也有些网卡支持用户配置mac地址)。 在linux操作系统中,我们可以采用ifconfig -a指令来查看mac地址:  ether的意思:以太,证明我的云服务器数据链路层采用的是以太网标准。  幸福从来不曾远离我们,只是有时候它会用试探的方式,看我们还在不在意。 —朱德庸 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/2301_77112634/article/details/140146028 
  • [技术干货] MQTT协议史上最全解析(纯干货分享)-转载
    MQTT是什么? MQTT协议全称是(Message Queuing Telemetry Transport),即消息队列遥测传输协议。  是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,并且该协议构建于TCP/IP协议之上,我们知道TCP协议本身就具有高可靠性的特点,因此基于其上的MQTT协议同样也是具有高可靠、低开销的特点,之所以低开销,是以为MQTT协议传输的最小的报文也只有两个字节。  在物联网开发中,MQTT不是唯一的选择,与MQTT互相竞争的协议有XMPP和CoAP协议等。  关于发布和订阅的概念我们拿抖音平台来举个例子,我们每一个用户就都是一个客户端,而抖音就是MQTT协议中的服务器,当我们(用户一)关注某一个视频发布者(用户二)时,这样一个关注的行为就可以理解为订阅;同时用户二也可以关注你,那么这就是相互订阅。当用户二发布作品的时候,这个作品是发布到了抖音平台,也就是我们现在的服务器,这个过程就是消息的发布。  在这里需要注意的是:用户二(客户端)发布的消息并不是直接发布给了用户一,而是发布到了抖音平台(服务器),由于用户一订阅了用户二的消息(相当于点了关注),所以抖音平台(服务器)就会向用户一推送这个消息(注意发布和推送的区别)。这就是MQTT协议订阅&发布的一个简单比喻。   上面是一个比喻,再来对比一下实际的MQTT协议通信模型:  实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。  MQTT传输的消息分为:主题(Topic)和负载(payload)两部分  Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload) payload,可以理解为消息的内容,是指订阅者具体要使用的内容 一些术语解释 网络连接 Network Connection  MQTT 使用的底层传输协议(TCP)基础设施。  客户端使用它连接服务端。 它提供有序的、可靠的、双向字节流传输。 应用消息 Application Message  MQTT 协议通过网络传输应用数据。应用消息通过 MQTT 传输时,它们有关联的服务质量(QoS) 和主题(Topic) 。  客户端 Client  使用 MQTT 的程序或设备。客户端总是通过网络连接到服务端。它可以  发布应用消息给其它相关的客户端。 订阅以请求接受相关的应用消息 取消订阅以移除接受应用消息的请求。 从服务端断开连接。 服务端 Server  一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。服务端  接受来自客户端的网络连接。 接受客户端发布的应用消息。 处理客户端的订阅和取消订阅请求。 转发应用消息给符合条件的已订阅客户端。 订阅 Subscription  订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(Session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。  主题名 Topic Name  附加在应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的一个副本给每一个匹配的客户端订阅。  主题过滤器 Topic Filter  订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。  会话 Session  客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。  控制报文 MQTT Control Packet  通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文) 用于传输应用消息。  MQTT特点 1.客户端使用它连接服务端。 2.它提供有序的、可靠的、双向字节流传输。 3.使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦。 4.消息传输不需要知道负载内容。 5.提供三种等级的服务质量QoS : 6.很小的传输消耗和协议数据交换,最大限度减少网络流量 7.异常连接断开发生时,能通知到相关各方。  消息质量(QoS)  MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。  QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息要么只会到达服务端一次,要么根本没有到达。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。  QoS 1:至少分发一次。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。(保证交付功能)  QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。  eg. 目前流行的共享单车智能锁,智能锁可以定时使用QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过一段时间又会再发送一次。之后用户可以通过App查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机App不断的发送解锁消息给单车锁,确保有一次消息能达到以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱了。  MQTT控制报文格式 MQTT 控制报文的结构  Fixed header    固定报头,所有控制报文都包含 Variable header    可变报头,部分控制报文包含 Payload    有效载荷,部分控制报文包含 注:固定报头必须有,可变报头和有效负载可以没有。  【固定报头】:第一个字节高4位是MQTT控制报文的类型 + 第一个字节低4位用于指定控制报文类型的标志位(只在PUBLISH发布消息时用到) + 剩余长度1~4个字节(剩余长度字段最大4个字节) 剩余长度包括可变报头和负载的数据。 剩余长度不包括用于编码剩余长度字段本身的字节数。 剩余长度字段使用一个变长度编码方案,对小于 128 的值它使用单字节编码。更大的值按下面的方式处理。 低 7 位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码 128 个数值和一个延续位(continuation bit) 。 剩余长度字段最大4个字节。  【可变报头】:可变报头的内容根据报文类型的不同而不同。 某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的报文标识符(Packet Identifier) 字段存在于在多个类型的报文里。 报文标志符(2字节):包含报文标识符的9个控制报文有:PUBLISH(QoS>0),PUBACK ,PUBREC ,PUBREL ,PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK  报文标志符的注意项: 1.客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。 2. SUBSCRIBE,UNSUBSCRIBE 和 PUBLISH(QoS 大于 0)控制报文 必须包含一个非零的 16 位报文标识符(Packet Identifier) 3. 客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。 4. QoS 设置为 0 的 PUBLISH 报文 不能包含报文标识符。 5.PUBACK, PUBREC, PUBREL 报文 必须包含与最初发送的 PUBLISH 报文相同的报文标识符。类似地,SUBACK 和 UNSUBACK 必须包含在对应的 SUBSCRIBE 和 UNSUBSCRIBE 报文中使用的报文标识符。 6. 作用:基于标志位来决定可变报头中是否需要包含这些字段  【有效载荷】:某些 MQTT 控制报文在报文的最后部分包含一个有效载荷,例如对于 PUBLISH 来说有效载荷就是应用消息。 可能包含有效载荷的控制报文有:CONNECT,PUBLISH,SUBSCRIBE,SUBACK,UNSUBSCRIBE  十四种控制报文 注:C->S指的是客户端到服务端,S->C指的是服务端到客户端,C<->S指的是两个方向都允许 0和15类型值保留无效。 表格中的值指的是固定报头第一个字节的高4位即MQTT控制报文的类型的值 PUBREC和PUBREL和PUBCOMP只有在QoS2等级时才存在  1号控制报文:CONNECT – 连接到服务端 注:客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文 必须是 CONNECT 报文 在一个网络连接上,客户端只能发送一次 CONNECT 报文。服务端 必须将客户端发送的第二个 CONNECT报文当作协议违规处理并断开客户端的连接。 CONNECT 报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive) 注:客户端用 8 位的无符号值表示协议的修订版本。对于 3.1.1 版协议,协议级别字段的值是 4(0x04)。如果发 现不支持的协议级别,服务端 必须给客户端发送一个返回码为 0x01(不支持的协议级别)的 CONNACK报文响应CONNECT 报文,然后断开客户端的连接 注:连接标志字节包含一些用于指定 MQTT 连接行为的参数。它还指出有效载荷中的字段是否存在。此连接标志将预示后面有效载荷的内容 注:服务端 必须验证 CONNECT 控制报文的保留标志位(第 0 位)是否为 0,如果不为 0 必须断开客户端连接  CONNECT控制报文的内容: 固定报头 MQTT 4位报文类型加4位保留位即10 ,剩余长度字节由后续的数据长度决定 可变报头 固定协议名6字节 长度00 04 + MQTT UTF-8编码 00 04 4D 51 54 54 协议级别1字节 04 代表3.1.1 版协议 连接标志1字节 C2 置位密码标志,用户名标志,清理会话标志 保持连接时间2字节 00 78 代表120秒 有效载荷 客户端ID数据长度2字节 + 客户端ID ASC码 + 用户名数据长度2字节 + 用户名 ASC码 + 密码长度2字节 + 密码 ASC码  注意!CONNECT 报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的连接标志决定是否 包含这些字段。如果包含的话, 必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码  报文举例: 10 | 25 | 00 04 4D 51 54 54 | 04 | C2 | 00 78 | 00 09 35 32 38 39 38 36 38 37 35 | 00 06 32 34 38 34 39 33 | 00 06 6B 66 62 73 6B 64  固定报头0x10表示客户端请求连接服务端 固定报头0x25表示可变报头和有效负载一共37个字节数据,本身不算在内 协议名00 04 4D 51 54 54 ,00 04表示4个字节 ,4D 51 54 54是MQTT 的 UTF-8 编码的字符串(也是ASC码值) 协议级别04客户端用 8 位的无符号值表示协议的修订版本。对于 3.1.1 版协议,协议级别字段的值是 4(0x04) 连接标志C2 保持连接 00 78 即120秒 00 09 35 32 38 39 38 36 38 37 35 设备ID ,9个字节,528986875的ASC码,以下相同 00 06 32 34 38 34 39 33 产品ID 248493 00 06 6B 66 62 73 6B 64 鉴权信息 kfbskd  2号控制报文:CONNACK – 确认连接请求 注意:服务端发送 CONNACK 报文响应从客户端收到的 CONNECT 报文。服务端发送给客户端的第一个报文 必须是 CONNACK 报文 如果客户端在合理的时间内没有收到服务端的 CONNACK 报文,客户端 应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。 CONNACK控制报文的内容: 固定报头 MQTT 4位报文类型加4位保留位即20 ,剩余长度字节由后续的数据长度决定 可变报头 两字节:连接确认标志(前7位必须为0,最低位取决于连接标志中的清理会话标志)+连接返回码(正常为00,否则指示故障) 有效载荷 无  报文举例: 20 02 01 00 固定报头20 02:20标志报文类型2,02剩余长度 可变报头01 00 有效负载无  3号控制报文:PUBLISH – 发布消息 PUBLISH 控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。 客户端使用 PUBLISH 报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。 服务端使用 PUBLISH 报文发送应用消息给每一个订阅匹配的客户端。  报文内容组成: 固定报头 第一个字节高4位报文类型3 ,低4位分别是DUP重发标志-QoS等级高位-QoS等级低位-保留位,第2个字节开始是剩余长度 可变报头 可变报头按顺序包含主题名(UTF-8编码)和报文标识符。主题名之前有两字节主题名的长度,报文标志符是两字节。 注意报文标志符只在QoS等级1和2的报文中有,等级0没有直接忽略 有效负载 有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。长度计算是剩余长度-可变报头的长度  关于DUP重发标志:如果 DUP 标志被设置为 0,表示这是客户端或服务端第一次请求发送这个 PUBLISH 报文。如果 DUP 标志 被设置为 1,表示这可能是一个早前报文请求的重发。 客户端或服务端请求重发一个 PUBLISH 报文时, 必须将 DUP 标志设置为 1 。对于 QoS 0 的消息,DUP 标志 必须设置为 0 。  关于QoS等级标志位:PUBLISH 报文不能将QoS所有的位设置为 1。如果服务端或客户端收到QoS所有位都为1的 PUBLISH报文,它必须关闭网络连接  关于保留位:如果客户端发给服务端的 PUBLISH 报文的保留(RETAIN)标志被设置为 1,服务端 必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。 如果客户端发给服务端的 PUBLISH 报文的保留标志位 0,服务端 不能存储这个消息也 不能移除或替换任何现存的保留消息。  报文举例: 30 0E 00 09 6B 66 62 5F 74 6F 70 69 63 31 32 33 QoS等级0 数据:123,等级0没有回复  32 10 00 09 6B 66 62 5F 74 6F 70 69 63 00 01 31 32 33 等级1 数据:123  34 10 00 09 6B 66 62 5F 74 6F 70 69 63 00 01 31 32 33 等级2 数据:123  这里面的主题名称是kfb_topic: 6B 66 62 5F 74 6F 70 69 63,主题名之前有两字节主题名的长度00 09 报文标志符只在QoS等级1和2的报文中有,例子中报文标识符是00 01  4号控制报文:PUBACK – 发布确认 PUBACK 报文是对 QoS 1 等级的 PUBLISH 报文的响应。  报文内容组成: 固定报头 报文类型40+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无  报文举例: 40 02 00 01 等级1的回复,表示签收 固定报头40 02:40标志报文类型4,02剩余长度 可变报头00 01 与等待确认的PUBLISH报文的报文标识符一致 有效负载无  5号控制报文:PUBREC – 发布收到(QoS 2 ,第一步) PUBREC 报文是对 QoS 等级 2 的 PUBLISH 报文的响应。(通常由接收方回复)它是 QoS 2 等级协议交换的第二个报文。  报文内容组成: 固定报头 报文类型50+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无  报文举例: 50 02 00 01 等级2的第一次服务器的回复,等级2有两次回复,客户端再次发送62 02 00 01,得到服务器的二次回复70 02 00 01表示成功 固定报头50 02:40标志报文类型5,02剩余长度 可变报头00 01 与等待确认的PUBLISH报文的报文标识符一致 有效负载无  6号控制报文:PUBREL – 发布 释放(QoS 2 ,第二步) PUBREL 报文是对 PUBREC 报文的响应。(通常是发送方发出)它是 QoS 2 等级协议交换的第三个报文。 注意!PUBREL 控制报文固定报头的第 3,2,1,0 位是保留位, 必须被设置为 0,0,1,0。服务端 必须将其它的任何值都当做是不合法的并关闭网络连接。  报文内容组成: 固定报头 报文类型62+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无  报文举例: 62 02 00 01 等级2 再次询问  7号控制报文:PUBCOMP – 发布 完成(QoS 2 ,第三步) PUBCOMP 报文是对 PUBREL 报文的响应。(通常由接收方回复)它是 QoS 2 等级协议交换的第四个也是最后一个报文。  报文内容组成: 固定报头 报文类型70+剩余长度02 可变报头 包含等待确认的 PUBLISH 报文的报文标识符两个字节。具体内容取决于之前的 PUBLISH 报文中的报文标志符。 有效负载 无  报文举例: 70 02 00 01 等级2的第二次回复,表示彻底完成  8号控制报文:SUBSCRIBE - 订阅主题 客户端向服务端发送 SUBSCRIBE 报文用于创建一个或多个订阅。 为了将应用消息转发给与那些订阅匹配的主题,服务端发送 PUBLISH 报文给客户端。 SUBSCRIBE报文也(为每个订阅)指定了最大的 QoS 等级,服务端根据这个发送应用消息给客户端。  报文内容组成: 固定报头 报文类型82+剩余长度 可变报头 报文标识符两个字节。 有效负载 主题过滤器长度两字节+主题过滤器(+【可包含多个】后一个主题过滤器长度两字节+主题过滤器+)服务质量等级1字节仅低两位有效  SUBSCRIBE 报文的有效载荷 必须包含至少一对主题过滤器 和 QoS 等级字段组合。 每一个过滤器后面跟着一个字节,这个字节被叫做服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大 QoS 等级。 
  • [技术干货] Linux之彻底结束脚本运行-转载
    要结束运行中的 shell 脚本以及它调用的所有子脚本,你可以通过几个步骤来确保它们都被终止。以下是一些建议:  结束所有脚本 如果你知道脚本的进程 ID(PID),可以直接使用 kill 命令结束该进程。如果你启动了一个脚本,并且它调用了其他脚本,你可以结束父脚本进程及其所有子进程。  kill -- -PID 其中 PID 是你想要结束的父脚本的进程 ID。-- 表示发送信号到该 PID 所在的进程组。这将发送一个 SIGTERM 信号到该进程组中的所有进程,尝试优雅地结束它们。  确定脚本是否结束 要检查进程是否已经结束,可以使用 ps 命令配合 grep 命令。  ps aux | grep 'script_name' 其中 script_name 是你的脚本文件名。如果进程已经结束,这个命令不应该返回任何关于该脚本的输出。如果你看到输出,那可能意味着有些进程仍在运行。  如果你不确定所有相关的进程 ID,或者想要更直观地结束这些脚本,你可以考虑以下方法:  通过脚本名称结束进程 使用 pkill 命令可以根据进程名称来发送信号,这样可以确保所有包含特定名称的进程都被结束。  pkill -f 'script_name' -f 参数允许 pkill 根据完整的命令行参数来匹配进程,而不仅仅是进程名。  检查是否还有进程在运行 使用 pgrep 命令来找出是否还有符合条件的进程在运行。  pgrep -f 'script_name' 如果仍有进程在运行,pgrep 会返回它们的 PID。如果没有匹配的进程,pgrep 不会返回任何内容。  请注意,pkill 和 pgrep 命令中的 -f 选项是非常强大的,因为它们会匹配所有包含指定字符串的命令行,所以请确保你提供了足够详细且唯一的脚本名称或路径,以避免意外终止无关的进程。  最后,强制结束进程(如使用 kill -9)应该是最后的手段,因为它不允许进程进行任何清理工作。在大多数情况下,首先尝试使用没有 -9 选项的 kill 命令来结束进程。 ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/hebtu666/article/details/139453609 
  • [技术干货] 【Linux】进程程序替换-转载
    想要弄明白为什么要有进程程序替换,我们首先要知道父进程创建子进程的目的是什么?想要子进程完成什么样的任务?  想让子进程执行父进程代码的一部分。 让子进程想办法,加载磁盘上的指定程序,然后执行新程序对应的代码和数据。(这就是我们所讲的进程程序替换) 一.什么是进程程序替换 进程程序替换顾名思义,就是将其他程序替换过来继续执行,主要是通过exec* 这类函数来帮助我们替换,直接来学这类函数。  二.怎样实现 进程程序替换函数其实不止一个函数,而是由六个以execl开头的函数构成的函数族,如下图:   其中,最常用的就是execl,我们就用execl为例讲解。我们理解了一个函数的使用方法和原理,对其他相关函数的学习就会起到事半功倍的效果。  execl函数       int execl(const char *path, const char *arg, ...);后边的“...”是可变参数列表,意为可以传入多个参数。    要完成进程替换需要哪些工作?  我们要找到要替换的程序所在路径,即找到。execl的第一个参数的作用就是传入替换程序的具体路径。 然后,我们要找到程序的执行方法,就是在命令行中如何执行,就要如何传入。除第一个外,其他参数都是用来传入执行方法的。执行方法传入完毕后,以null结尾。 实例:     execl("/usr/bin/ls","ls","-a","-l","--color=auto",NULL);   单进程程序替换完整代码 #include<stdio.h> #include<unistd.h> int main() {     printf("start.......\n");     execl("/usr/bin/ls","ls","-a","-l","--color=auto",NULL);     printf("end.......\n");   }  执行:    父子进程程序替换完整代码 代码:  #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> int main() {     pid_t id=fork();     if(id==0)     {         printf("start.......\n");         execl("/usr/bin/ls","ls","-a","-l","--color=auto",NULL);         printf("end.......\n");       }     else     {         int status=0;         pid_t fd=waitpid(id,&status,0);         if(fd==id)         {             printf("pid:%d,exist code:%d,sig code:%d\n",fd,(status>>8)&0xFF,status&0x7F);           }     } } 运行结果:    基于execl谈程序替换原理 1.单进程 我知道,此刻的大家心里一定有诸多的问题,比着急,我们一一探讨。  首先来看这张我们非常熟悉的图:   想必大家学到现在,这张图都不陌生了。  程序替换的原理就是:把物理内存中原来的代码和数据替换成目标程序的代码和数据,也就是说:调用execl之后的代码和数据都被替换掉了,有可能还要发生映射关系的改变。  程序替换的本质就是:将指定的代码和数据,加载到指定的位置,然后覆盖自己的代码和数据。  为什么在单进程的运行结果中,第一条打印语句执行了,而第二条打印语句没有执行?  因为在execl语句之后的代码和数据,在物理内存中被目标程序的代码和数据替换掉了(也就是覆盖掉了),接下来执行的是目标程序的代码和数据,因为这个原因,第二条print语句没有被执行。  进程替换的时候,有没有创建新的进程?  没有,进程替换仅仅将物理内存中的代码和数据做了替换。虚拟内存和PCB表没有发生任何改变。   关于返回值问题,为什么程序替换成功,没有返回值;程序替换失败返回值为-1?   不需要,因为:exec类函数只要返回了,就一定是失败的。  成功的情况下,就和接下来的代码无关了(不会再执行下面关于返回值的判断了),返回值也就变得毫无意义。 失败的情况下,仍然执行接下来的代码,可能仍然需要利用返回值做某些判断,这时的返回值仍然有意义。 2.多进程  在理解单进程程序替换的基础上,多进程的程序替换就容易理解了。  我们先要达成一个共识:写时拷贝这种机制,不仅仅是对数据,对代码也有写时拷贝策略。   子进程被创建,子进程继承父进程的PCB,虚拟内存,页表,并和父进程共同使用一份代码和数据。一方对代码和数据进行修改时,为该方创建一份新的内存空间,供其修改。这就是写时拷贝。  假如:子进程要对代码进行修改,操作系统为其分配一段新的内存空间,让其存放新的代码和附带数据,依旧保持内存的独立性。  所以,依旧还是两个进程在运行,并未产生新的进程。    三.其他程序替换类函数(除execl)  除execl外,其余五个函数的返回值问题,所属头文件,作用等等和execl都是相同的,差异主要体现在参数列表方面。详解如下:   1.execlp 程序替换类函数的函数名称共同部分为:exec。然后后面再加不同的字母,显然这些字母代表着不同的含义:  l:list。表示将参数逐个传入。  p:path。如何找到程序的功能。带有p字符的函数,不用传入程序所在的具体路径,只要传入程序名,函数会自动在PATH里的路径下,进行可执行程序的查找。   想必大家都知道execlp函数的使用方法了吧  如下:  #include<stdio.h> #include<unistd.h> int main() {     printf("start.......\n");     execlp("ls","ls","-a","-l",NULL);     printf("end.........\n");   } 在execl函数的参数中,有两个“ls”,冲突吗?不冲突,第一个是告诉系统我要执行谁,一个是告诉系统我要如何执行。  运行一下    2.execv v:vector,可以将执行参数放入数组中,进行同一传递。而不用使用可变参数方案。  #include<stdio.h> #include<unistd.h> int main() {     char *const argv_[]={         "ls",         "-a",         "-l",         "--color=auto"     };     printf("start.......\n");     execv("/usr/bin/ls",argv_);     printf("end.........\n");   } 运行一下:     3.execle e:传入自定义环境变量,即可以查询系统中的环境变量。  代码实例:这次我们需要创建3个文件,makefile,mybin.c,getenv.cc  makefile:  .PHONY:all all:my.out mybin   my.out:getenv.cc     g++ getenv.cc -o my.out -std=c++11 mybin:mybin.c     gcc mybin.c -o mybin -std=c++11 .PHONY:clean  clean:     rm -f my.out mybin getenv.cc  #include<stdio.h> #include<unistd.h> #include<stdlib.h> int main() {     printf("start.......\n");     putenv("myenv=1111122222");//作用为:向环境变量中导入自定义的环境变量。     execl("./mybin","mybin",NULL,environ);   } mybin.c  #include<stdio.h> #include<stdlib.h> int main() {       printf("PATH:%s\n",getenv("PATH"));       printf("PWD:%s\n",getenv("PWD"));       printf("myenv:%s\n",getenv("myenv"));         printf("hello world\n");       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");   } 4.execvp和execvpe 这两个函数其实就是前面我们所说的几个函数传参方式糅杂在一起而已,所以这里就不在讲了。  5.总结   刚刚好我们一共提到了6个函数,其实这些函数都是C语言封装的函数,这些函数都是由一个叫execve的函数封装而来。execve这个函数是系统调用接口,感兴趣的可以自己查一下。  四.如何利用程序替换函数调用自己写的程序 我们自己写的程序也是可执行程序,理论上也是可以使用程序替换函数进程程序替换的。  接下来,我们验证一下:创建三个文件:makefile,mybin.c getenv.cc  makefile:  .PHONY:all all:my.out mybin   my.out:getenv.cc     g++ getenv.cc -o my.out -std=c++11 mybin:mybin.c     gcc mybin.c -o mybin -std=c++11 .PHONY:clean  clean:     rm -f my.out mybin  mybin.c  #include<stdio.h> #include<unistd.h> int main() {     printf("start.......\n");     execl("./mybin","mybin",NULL);      }   getenv.cc  #include<stdio.h> int main() {       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");       printf("hello world\n");   } 运行一下:   其实,无论是什么语言写的程序,只要是可执行程序,都可以使用进程替换函数执行。  五.制作一个简单的shell程序 代码:  #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h> #include<string.h> #define NUM 1024 char lineCommand[NUM]; char *myargv[24]; int main() {          while (1)     {            printf("用户名@主机名 当前路径# ");         //printf("%s@%s %s####",getenv("USER"),getenv("HOST"),getenv("PWD"));         fflush(stdout);//刷新缓冲区         char *s = fgets(lineCommand, sizeof(lineCommand) - 1, stdin);         assert(s != nullptr);         (void)s;         lineCommand[strlen(lineCommand)-1]=0;         myargv[0]=strtok(lineCommand," ");//根据空格分割字符串。         int i=1;         while(myargv[i]=strtok(NULL," "));           pid_t fd=fork();//创建新进程         if(fd==0)         {             execvp(myargv[0],myargv);//子进程进行程序替换             printf("命令非法,请重新输入\n");                      }         waitpid(fd,NULL,0);         }     return 0;   } 运行一下     写到最后,因水平有限,难免会有错误,请各位指正!! ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/2301_79181030/article/details/139578689 
  • [技术干货] 探索SRS-GB28181:一款强大的国标GB28181视频服务器-转载
    探索SRS-GB28181:一款强大的国标GB28181视频服务器 项目地址:https://gitcode.com/ossrs/srs-gb28181  项目简介 SRS-GB28181 是一个开源的、基于 SRS (Simple, Robust and Scalable) 的 GB28181 视频服务器,专为满足中国国家标准 GB/T 28181-2016 而设计。该项目提供了一套完整的解决方案,用于搭建和管理符合国标的监控录像平台,支持视频流媒体传输、实时通信、设备管理和录像回放等功能。  技术分析 SRS-GB28181 在 SRS 的基础上,实现了对 GB28181 协议的支持。以下是其主要的技术亮点:  高性能:基于 SRS 的核心,SRS-GB28181 具有优秀的性能表现,能够处理大量并发连接和数据传输。 GB28181 协议实现:全面支持 GB28181 协议中的注册、发现、控制、播放等操作,保证了与其他 GB28181 设备的兼容性。 多媒体支持:支持 RTSP/RTP/RTMP 等多种协议,可与各种摄像头和编码器无缝对接。 云台控制:支持 PTZ(Pan-Tilt-Zoom)云台控制功能,方便远程调整摄像机视角。 录像与回放:内置录像机制,并提供录像文件查询和回放接口。 RESTful API:提供了丰富的 RESTful API 接口,便于第三方系统集成和扩展。 分布式架构:易于扩展至分布式部署,以应对大规模监控场景。 应用场景 SRS-GB28181 可广泛应用于以下领域:  智慧城市:作为监控系统的核心组件,提供高清视频流传输和实时视频查看能力。 智慧安防:在小区、商场、学校等场所构建安全监控网络,提高安全管理效率。 远程监控:适用于家庭、仓库、工厂等远程监控需求,通过手机或电脑实时查看监控画面。 交通监控:在道路、路口、停车场等地方进行交通状况监测,助力智能交通系统建设。 企业办公:为企业内部提供安全的监控环境,保障财产安全。 特点 开源免费:遵循 Apache 2.0 开源许可,无商业限制,鼓励社区贡献和二次开发。 易用性:提供详细的文档和示例,降低部署和使用的难度。 稳定可靠:经过实际项目验证,具有高可用性和稳定性。 持续更新:活跃的开发者社区,定期发布新版本,修复问题并添加新特性。 结语 如果你正在寻找一个高效、易用且符合国标的视频服务器方案,SRS-GB28181 绝对值得尝试。无论是小型监控系统还是大型智慧城市项目,它都能凭借其实力成为你的得力助手。现在就加入 SRS-GB28181 社区,开始你的国标视频服务之旅吧! ————————————————                              版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                          原文链接:https://blog.csdn.net/gitblog_00045/article/details/137368518 
  • [专题汇总] 2024年5月嵌入式项目开发专题总汇
    一、前言在嵌入式系统和物联网领域,多种技术和工具的应用为实现智能化和互联互通提供了强大支撑。本篇文章作为一个合集,总结华为云物联网论坛5月份的技术文章。 本文对基于STM32的轻量级Web服务器设计、华为云IOT物联网平台使用、C语言结构体位域与宏定义、Linux下的调试与文件操作、以及DS1302和DS18B20等硬件接口的使用,以及C语言新标准特性进行简要总结。基于STM32的轻量级Web服务器设计是嵌入式系统中实现远程监控和数据交互的重要手段。通过STM32微控制器,结合嵌入式Web服务器软件,可以实现设备的网络化,使得用户可以通过互联网远程访问和控制设备。这种设计在智能家居、环境监测等领域具有广泛应用。华为云IOT物联网平台为物联网设备的接入和管理提供了强大的支持。通过新建产品、设备等步骤,可以将设备连接到云平台,实现数据的采集、存储、分析和可视化。以环境监测项目为例,通过云平台可以实时查看环境参数,并进行相应的控制操作。在C语言编程中,结构体位域(位段)是一种有效的数据压缩技术,它允许在结构体中定义占用位数较少的成员,从而节省存储空间。而宏定义则是一种常用的代码复用和条件编译技术,通过定义宏可以实现代码的灵活配置和条件性编译。Linux操作系统提供了丰富的工具和命令用于程序的调试和文件操作。GDB(GNU调试器)是Linux下常用的调试工具,它可以帮助开发人员定位和解决程序中的错误。而命令行重定向运算符、find命令、文件创建删除等操作则提供了强大的文件处理能力。此外,Linux下的系统目录结构也体现了其强大的系统管理能力。在Linux系统中,环境变量的设置对于程序的运行至关重要。通过设置可执行文件和库文件的环境变量,可以使程序正确地找到所需的文件。这对于程序的移植和部署具有重要意义。DS1302实时时钟芯片和DS18B20温度传感器是常用的硬件接口模块。DS1302可以提供准确的时间信息,而DS18B20则可以测量环境的温度。通过与51单片机等微控制器的配合,可以实现时间的显示和温度的测量等功能。C语言的新标准C99和C11为C语言的发展注入了新的活力。这些新标准增加了许多新的特性和语法,如变长数组、复数类型、泛型编程等,使得C语言更加灵活和强大。以下是文章概览及其核心价值:基于STM32的轻量级Web服务器设计:揭示了如何在资源有限的微控制器上实现网络通信,构建自己的Web服务器,为物联网设备提供远程控制与监控的能力。华为云IoT物联网平台-标准版(最新)新建产品、设备过程讲解:以环境监测项目为实例,手把手教你利用华为云强大的IoT平台,从零开始创建产品模型至设备接入,展示了云端集成的便捷与高效。C语言结构体位域(位段)使用:深入浅出地介绍了位域这一高效数据结构,帮助开发者在存储敏感或资源受限环境下优化内存使用。C语言使用宏定义控制日志输出:通过巧妙的宏定义技巧,实现了灵活的日志管理策略,让调试信息的开关变得轻松自如,极大提升了开发效率。Linux下gdb调试方法:详尽讲解了在Linux环境下使用gdb这一强大调试工具的技巧,助力开发者快速定位并修复代码问题。Windows下控制台终端颜色设置办法:为提升命令行界面的友好度,分享了简单易行的方法,让终端输出更加多彩且易于阅读。Linux下命令行重定向运算符的使用办法:介绍了重定向的强大功能,让数据流操控变得直观且高效,是每个Linux用户必备的技能。Linux下使用find命令使用介绍:深入探讨了find命令的复杂用法,无论是日常文件管理还是自动化脚本编写,都是不可或缺的知识点。Linux系统目录对应的功能介绍:全面解析了Linux系统目录结构,帮助读者理解各目录的作用,为系统管理打下坚实基础。设置可执行文件和库文件的环境变量:阐述了环境变量配置的重要性及其实现步骤,确保应用程序能找到其依赖的库文件和执行路径。DS1302实时时钟芯片与DS18B20温度传感器的51单片机应用:两篇文章分别介绍了如何利用经典单片机与外设进行精确时钟管理和温度测量,是硬件交互的入门佳作。C99与C11标准新特性概览:紧跟语言发展步伐,概述了现代C语言标准的重要更新,为开发者升级代码风格与效率提供了方向。二、技术文章总结【1】基于STM32的轻量级Web服务器设计cid:link_0探讨了如何在STM32微控制器上实现一个高效的Web服务器,展示了从硬件配置到软件实现的全过程,为物联网设备的网络互联提供了实践指导。【2】华为云IOT物联网平台-标准版(最新)新建产品、设备过程讲解(以环境监测项目为例)cid:link_1以最新的华为云物联网平台为背景,通过环境监测项目实例,详细步骤讲解了如何创建产品与设备,对接云端服务,为读者提供了从概念到实施的完整视图。【3】C语言结构体位域(位段)使用cid:link_5深入剖析C语言中的结构体位域特性,讲解了如何高效利用内存,这对于资源受限环境下的编程尤为重要。【4】 C语言使用宏定义控制日志输出cid:link_6展示了宏定义在日志系统中的灵活运用,使代码调试和维护变得更加智能和高效。【5】 Linux下gdb调试方法cid:link_7介绍了Linux环境下强大的gdb调试器使用方法,助力开发者高效定位代码错误,提升开发效率。【6】 windows下控制台终端颜色设置办法cid:link_8为提升命令行用户体验,讲解了如何在Windows控制台中设置颜色,使输出更加丰富和易读。【7】Linux下命令行重定向运算符的使用办法cid:link_2详解了Linux命令行中重定向的强大功能,帮助用户更好地管理数据流和自动化脚本编写。【8】 Linux下使用find命令使用介绍cid:link_9通过实例介绍了find命令的高级用法,让文件查找和管理变得更加高效。【9】 Linux下命令行文件创建删除、目录创建删除cid:link_10总结了在Linux命令行下创建、删除文件和目录,以及相关管理技巧,强化系统操作能力。【10】Linux下系统目录对应的功能介绍cid:link_11系统性地介绍了Linux系统目录结构及其功能,加深对操作系统内部机制的理解。【11】 Linux下如何设置可执行文件和库文件的环境变量?cid:link_12指导如何配置环境变量以确保程序能找到其所需的执行文件和库文件,对软件部署和运行环境管理至关重要。【12】 DS1302实时时钟芯片完整使用介绍(配合51单片机)cid:link_3【13】 DS18B20温度传感器完整使用介绍(配合51单片机)cid:link_4【14】 C语言C99标准、C11标准新增加的特性cid:link_13概述了C语言在C99和C11标准中的重要更新,为开发者升级编程实践提供了理论依据。
  • 物联网中的数据加密技术
    物联网中的数据加密技术概述物联网(IoT)作为一个由无数互联设备组成的庞大网络,正逐渐渗透至我们生活的方方面面。随着技术的迅猛发展,物联网设备所处理的数据量呈现爆炸性增长,这也使得数据的安全性成为了不容忽视的问题。在物联网中,数据加密技术扮演着至关重要的角色,它不仅能保护数据在传输过程中的机密性,还能确保数据的完整性和可用性。物联网中的安全威胁与挑战物联网设备的安全威胁主要来源于数据在传输过程中可能遭遇的窃听、篡改以及未经授权的访问。例如,智能家居系统中的摄像头、工业控制系统中的生产数据、甚至是智能交通系统中的车辆通信,都需要通过加密技术来保护个人隐私和敏感信息。加密技术的应用在物联网中,加密技术的应用十分广泛,包括但不限于以下几个方面:数据传输加密:使用SSL/TLS协议确保数据在网络中传输的安全性,防止数据被窃听或篡改。设备身份认证:通过X.509证书等技术实现设备身份的认证,确保设备间的通信安全。数据存储加密:对存储在物联网设备上的数据进行加密,以防数据泄露。远程访问控制:使用加密技术对远程访问物联网设备的通道进行保护,防止未授权访问。加密技术的选择在选择物联网中的加密技术时,需要考虑到算法的效率、安全性和适用性。对称加密算法如AES、DES因其速度快而被广泛使用,而非对称加密算法如RSA则因其安全性高而在密钥交换等场景中占有一席之地。此外,为了适应物联网设备的资源限制,轻量级加密算法也被提了出来,这类算法具有较低的计算复杂度和内存占用。物联网中的加密技术在物联网(IoT)中,数据传输的安全性至关重要,因为它涉及到个人隐私、商业秘密和国家安全等多个层面。为了确保数据传输的安全,加密技术发挥着关键作用。以下是一些在物联网中强化加密技术的方法:使用安全套接层(SSL)和传输层安全(TLS)协议:SSL和TLS是广泛应用于物联网的安全数据传输协议,它们基于公钥基础设施(PKI)和对称密钥加密算法,能够保护数据的机密性、完整性和身份认证。虚拟专用网络(VPN)技术:VPN技术可以在公共网络上建立一个加密的通信隧道,确保数据传输的机密性和完整性,避免数据在传输过程中被窃听和篡改的风险。数据加密传输:数据加密传输是指对数据进行加密处理,然后再进行传输,以确保数据传输的安全性。在物联网中,数据加密传输是一种保证数据传输安全的必要手段。其加密技术有很多种,包括对称加密、非对称加密、混合加密等。密钥管理和加密算法的选择:密钥管理是保证数据安全的关键环节,涉及到密钥的产生、存储、分发、更换和销毁等环节。有效的密钥管理系统能够确保密钥的安全性,防止密钥泄露或被滥用。此外,密钥生命周期管理也是确保系统安全的重要措施。加密技术的应用场景:在物联网领域,数据加密技术广泛应用于设备身份验证、数据传输、数据存储和远程访问控制等方面。例如,在智能家居系统中,用户的数据通信需要经过加密以保护个人隐私;在工业物联网中,生产数据的加密传输可避免敏感信息的泄露。加密技术的挑战与对策:随着物联网技术的快速发展,加密技术也面临着新的挑战,如密钥管理的复杂性、加密算法的局限性、缺乏统一的标准和规范等。为了应对这些挑战,需要简化证书认证流程并采用自动化的方式进行证书审核与管理,采用先进的加密技术和算法进一步提高数据安全水平。通过上述措施,可以有效地强化物联网中的加密技术,确保数据传输的安全性,保护用户数据不被未授权访问和篡改。随着技术的不断进步,未来的物联网加密技术将更加强大和可靠,以应对日益复杂的安全威胁。深入研究物联网中对称加密和非对称加密有什么区别及其各自的适用场景?对称加密与非对称加密的区别对称加密和非对称加密是两种常用的加密方法,它们在密钥管理、安全性和效率方面有所区别。对称加密密钥管理:对称加密使用同一个密钥进行加密和解密,因此密钥的分发和管理是一个重要挑战。安全性:对称加密的安全性依赖于密钥的保密性,如果密钥泄露,加密的信息就会被破解。效率:对称加密算法通常较快且高效,特别适合处理大量数据。非对称加密密钥管理:非对称加密使用一对密钥,即公钥和私钥。公钥用于加密信息,私钥用于解密。公钥可以公开分享,但私钥必须保密。安全性:非对称加密提供了更高级别的安全性,因为即使公钥是公开的,没有私钥也无法解密信息。效率:非对称加密的加密效率较低,尤其是在处理大量数据时。各自的适用场景对称加密的适用场景文件传输:对称加密适用于需要高速传输大量数据的场景,如文件加密、数据库加密。网络数据传输加密:对称加密也常用于网络数据传输加密等需要高速加密大量数据的场景。非对称加密的适用场景数字签名:非对称加密常用于数字签名,确保数据的完整性和来源。密钥交换:非对称加密用于安全地交换对称加密的密钥,如SSL/TLS协议中的密钥交换。身份验证:非对称加密支持第三方验证,即验证消息是否来自发送方。在实际应用中,对称加密和非对称加密往往结合使用,以达到既安全又高效的加密通信。例如,在HTTPS通信中,非对称加密用于安全地交换对称加密的密钥,之后的通信则使用对称加密来保证数据传输的效率.物联网设备在进行数据传输时通常使用哪些类型的加密算法?物联网设备在进行数据传输时通常使用以下几种类型的加密算法:对称加密算法对称加密算法使用相同的密钥进行加密和解密。常见的对称加密算法包括:Triple DES (3DES):一种数据加密标准,也称为三重数据加密算法(TDEA)。高级加密标准 (AES):例如使用256位密钥的AES-256。非对称加密算法非对称加密算法使用一对密钥,即公钥和私钥进行加密和解密。常见的非对称加密算法包括:RSA:一种广泛使用的非对称加密算法。数字签名算法 (DSA):用于验证消息的完整性和真实性。哈希算法哈希算法是一种将任意长度的消息压缩到固定长度摘要的算法,常用于消息认证、数字签名等领域。常见的哈希算法包括:MD5:一种较早的哈希算法,现在已不再推荐使用。SHA系列:包括SHA-1、SHA-256等,是目前更安全的哈希算法。数字证书数字证书是一种用于身份验证和信息加密的电子凭证,常用的数字证书有X.509证书等。物理层安全物理层安全是指利用物理特性实现信息安全,例如使用物理层加密技术将信号转换为不可解密的信号。这些加密算法可以用于保护设备之间的通信,防止信息泄露和攻击,从而提高物联网的信息安全性.物联网中密钥管理的常见挑战有哪些,以及如何解决?物联网中密钥管理的常见挑战物联网中的密钥管理面临多方面的挑战:设备多样性:物联网设备种类繁多,形态各异,这给密钥的管理和安全带来了一定的困难。资源限制:物联网设备通常资源有限,如何在有限的资源下实现有效的密钥管理是一个重要的问题。通信安全:如何保障物联网设备之间的通信安全,防止密钥的泄露和攻击,也是密钥管理需要解决的重要问题。密钥更新:在密钥更新过程中,如何确保更新的密钥安全、准确、完整的传输到所有设备是一个难题。密钥协商:在资源受限的设备上实现高效的密钥协商是一个挑战。现有的许多密钥协商协议在安全性、效率或可扩展性方面存在不足。解决方案针对上述挑战,一些解决方案被提出:轻量级加密算法:针对资源受限的设备,可以使用轻量级加密算法,如椭圆曲线密码学,这种算法可以在资源有限的环境中运行,同时提供足够的保护。分布式密钥管理:这种方法通过将密钥管理的责任分配给网络中的多个节点,以减轻单个节点的负担。例如,可以使用分布式密钥管理方案,如基于P2P的密钥协商协议,以提高网络的鲁棒性和安全性。安全协议:使用专门设计的安全协议可以应对各种安全威胁。例如,使用基于公钥的加密协议,可以在设备之间建立安全的通信通道,防止数据泄露和未经授权的访问。访问控制策略:为了更有效地控制对设备和数据的访问,可以使用基于属性的访问控制策略。例如,基于属性的访问控制模型可以根据设备的属性(如位置、类型等)来决定是否允许其访问特定的资源。未来研究方向未来的研究可能会关注以下几个方面:智能密钥管理:利用人工智能技术,使密钥管理更加智能化,能够自适应地应对各种复杂的网络环境和攻击。高效密钥管理:随着加密算法和密码技术的进步,提高密钥管理的效率,以适应物联网设备的高效运行。安全密钥管理:未来的密钥管理方案将更加注重安全性,包括使用更强大的加密算法,以及实施更为严格的安全策略,以保护物联网设备和数据的安全。结论综上所述,物联网中的数据加密技术是确保数据安全的关键。随着物联网技术的不断发展,加密技术也在不断地进化以应对新的安全挑战。无论是对称加密、非对称加密还是轻量级加密算法,都在各自的领域内发挥着重要作用。未来,随着技术的进一步发展,我们有理由相信会有更多高效、安全的加密技术出现在物联网领域,为我们的数据保驾护航。
  • [专题汇总] 5月IOT问题总结合集
    【1】ESP8266通过串口AT连接华为云出错cid:link_4【2】为什么小熊派nano开发板连接华为云平台案例,会出现设备未激活的情况cid:link_5【3】上海一iot设备接入就是没有endpoint的吗cid:link_6【4】IoTDA应用侧API如何操作自建实例cid:link_7【5】能否在华为云上搭建数据库,实现嵌入式设备接入华为云后,将采集到的数据存储到华为云数据库中cid:link_0【6】IOT物联网接入cid:link_1【7】RT-Thread可以通过MQTT连接到华为云吗?cid:link_2【8】liteOS移植编译后bin文件大小不对cid:link_8【9】stm32运行BC35上传信息到Iot平台一段时间后,就上传不上去了,隔一段时间又可以上传了是什么问题?cid:link_9【10】物联网面临哪些主要的数据安全性问题?cid:link_10【11】为何需要定期发布设备更新以修复安全漏洞?cid:link_3【12】在物联网中,如何强化加密技术来确保数据传输安全?cid:link_11【13】物联网行业中为何存在互操作性问题?如何解决?cid:link_12【14】物联网中缺乏哪些标准化协议可能导致兼容性问题?cid:link_13
  • [分享交流] MP3、DVD等传统电子产品的逐渐消失已经成为科技发展的一种趋势。那么,下一个可能被淘汰的电子产品会是什么呢?让我们一起来探讨一下,你认为下一个会被淘汰的电子产品是什么?
    MP3、DVD等传统电子产品的逐渐消失已经成为科技发展的一种趋势。那么,下一个可能被淘汰的电子产品会是什么呢?让我们一起来探讨一下,你认为下一个会被淘汰的电子产品是什么?
总条数:7138 到第 页
上滑加载中