基于MQTT通信开发的失物招领小程序

项目架构设计

这个项目采用前后端分离的方式,重新设计了两条链路来支撑程序的信息获取和传递

  1. 前端的小程序页面再启动页面渲染时,直接通过DBAPI从后端数据库获取信息,直接渲染在小程序中
  2. 项目中给DBAPI的定位是快速从后端获取信息,但是无法修改数据库,DBAPI和数据库同时及都部署在后端服务器,可以实现信息的快速交互,其中没有网络通信也保证信息安全
  3. 当小程序用户发布失物,或者是提交寻得失物,删除物品或者是用户注册等需要修改数据库的场景时,他们的信息会通过MQTT通信的方式经过小程序专用的socket连接发送给服务器
  4. 然后MQTT服务器会收到这些消息,然后分发给订阅了对应主题消息的客户端
  5. 我们开发的客户端订阅了所有小程序的消息,这个客户端会基于小程序发送的消息主题,选择对应函数,然后基于消息内容进一步去操作后端数据库,实现数据库的存储和修改

数据库设计

设计了以下两个表来存储项目的业务数据

失物表存储了丢失物品相关的信息:失物ID-id;丢失用户名-username;当前状态-type;丢失地点-area;照片-photo

用户表存储了用户相关的数据:学生ID-studentid;用户名-username;手机号-phonenumber

MQTT通信客户端

MQTT服务器所谓一个独立的中枢服务器,依赖于EMQX部署。而基于MQTT通信和数据库做交互,需要编写一个新的通信客户端。

客户端基于配置文件config.json运行,需先填写相关配置信息,客户端会读取并运行

	{  "database server":{  "host":"",  "port":3306,  "user":"",  "password":"",  "database":""  },  "mqtt server":{  "host":"",  "port":1883,  "other_topic":[  ]  }  }  

客户端要具备连接MQTT服务器和操作数据库的功能,为此做了以下设计:

1. 程序会识别系统和输出当前平台的运行状况,然后尝试连接到MySQL数据库。如果连接失败,程序会抛出错误并停止运行。

2. 然后,程序创建了一个MQTT客户端,尝试连接到MQTT代理服务器。如果连接失败,程序会记录错误并停止运行。

3. 定义了一个消息处理函数,该函数会在接收到MQTT消息时被调用。这个函数根据消息的主题进行不同的处理,包括插入数据到数据库、更新数据库中的数据、打印日志等。

4. 客户端程序将订阅一些MQTT主题,包括"lost"、"find"、"signup"、"delete"、"exit"、"error"。当这些主题有新的消息时,消息处理函数会被调用。这里设计了高并发处理,当由多个消息时会自动生成子线程去处理,同时对数据库的操作加锁,防止重复操作数据库

5. 当接程序收到中断信号时,程序会断开MQTT连接并退出。

6. 程序带有详细的日志功能,除了在终端会实时输出信息之外还会保存日志文件,方便后续追踪错误

7. 通用性也是我们开发这个客户端的目标,程序交叉编译后的程序可以运行在不同的操作系统和处理器架构上,利于部署到物联网设备

处理消息和SQL语句相关代码

import ("database/sql""log""strconv""strings"mqtt "github.com/eclipse/paho.mqtt.golang"
)// 处理 "lost" 主题的消息
func handleLostTopic(payload string, db *sql.DB) {parts := strings.Split(payload, ",")if len(parts) == 4 {user := parts[0]datatype := "lost"name := parts[1]area := parts[2]photo := parts[3]dbMutex.Lock() // 加锁// 往数据库中存储失物信息_, err := db.Exec("INSERT INTO sutff (username, type, name, area, photo) VALUES (?, ?, ?, ?, ?)",user,datatype,name,area,photo,)dbMutex.Unlock() // 解锁if err != nil {log.Fatal(err)}log.Printf("Lost item add to database {User: %s, Name: %s, Area: %s}\n",user,name,area,)}
}// 处理 "delete" 主题的消息
func handleDeleteTopic(payload string, db *sql.DB) {id, err := strconv.Atoi(payload)if err != nil {log.Fatal(err)}// 删除数据库中指定物品dbMutex.Lock()_, err = db.Exec("DELETE FROM sutff WHERE id = ?", id,)dbMutex.Unlock()if err != nil {log.Fatal(err)}log.Printf("Lost item with ID:%d has been deleted\n", id)
}// 处理 "find" 主题的消息
func handleFindTopic(payload string, db *sql.DB) {id, err := strconv.Atoi(payload)if err != nil {log.Fatal(err)}// 更新数据库中的数据dbMutex.Lock()_, err = db.Exec("UPDATE sutff SET type = ? WHERE id = ?","find",id,)dbMutex.Unlock()if err != nil {log.Fatal(err)}log.Printf("Lost item with ID:%d has been marked as found\n", id)
}// 处理 "signup" 主题的消息
func handleSignupTopic(payload string, db *sql.DB) {parts := strings.Split(payload, ",")if len(parts) == 3 {studentid := parts[0]username := parts[1]phonenumber := parts[2]dbMutex.Lock()// 查询数据库中是否已经存在具有相同 studentid 的用户var existingUser stringerr := db.QueryRow("SELECT studentid FROM user WHERE studentid = ?", studentid).Scan(&existingUser)// 如果查询结果返回了一个或多个记录,那么我们就不需要再插入新的用户if err == nil {dbMutex.Unlock()log.Printf("User with studentid %s already exists\n", studentid)return}// 往数据库中存储新用户信息_, err = db.Exec("INSERT INTO user (studentid, username, phonenumber) VALUES (?, ?, ?)",studentid,username,phonenumber,)dbMutex.Unlock()if err != nil {log.Fatal(err)}log.Printf("New user add to database {User: %s, Name: %s, Phone: %s}\n",studentid,username,phonenumber,)}
}// 处理接收到的消息
func handleMessage(client mqtt.Client, msg mqtt.Message, db *sql.DB) {getmsg := string(msg.Payload())if msg.Topic() == "lost" {lastComma := strings.LastIndex(getmsg, ",")if lastComma != -1 {getmsg = getmsg[:lastComma] + ",BASE64(photo)"}}log.Printf("Recevie topic[%s]  message: %s\n", msg.Topic(), getmsg)payload := string(msg.Payload())// 根据主题处理消息switch msg.Topic() {case "lost":handleLostTopic(payload, db)case "delete":handleDeleteTopic(payload, db)case "find":handleFindTopic(payload, db)case "signup":handleSignupTopic(payload, db)case "exit":log.Println("remot-eclient log out safely.")case "error":log.Println("remot-eclient lost connection, please try again later.")}// 可以基于此位置进一步开发,处理更多的主题
}

构建MQTT通信相关代码

import ("database/sql""strconv""strings""sync""time""log"mqtt "github.com/eclipse/paho.mqtt.golang"
)// 项目必须的主题
var extraTopics = []string{"lost","delete","find","signup","exit","error",
}// 创建MQTT客户端
func createMqttClient(config Config) mqtt.Client {opts := mqtt.NewClientOptions()broker := strings.Join([]string{config.MqttServer.Host,strconv.Itoa(config.MqttServer.Port),},":",)opts.AddBroker(broker)timestamp := time.Now().Unix()clientID := strings.Join([]string{"receiveclient_",strconv.FormatInt(timestamp, 10),},"",)opts.SetClientID(clientID)client := mqtt.NewClient(opts)log.Printf("Created MQTT client with ID: %s\n", clientID)// 建立连接token := client.Connect()if token.Wait() && token.Error() != nil {log.Fatal(token.Error())}// 打印连接到服务器的 IP 和端口log.Printf("Connected to MQTT broker at: %s\n", broker)return client
}// 订阅主题
func subscribeTopics(client mqtt.Client, config Config, db *sql.DB) {messageHandler := func(client mqtt.Client, msg mqtt.Message) {// 启动一个新的goroutine来处理这个消息go handleMessage(client, msg, db)}// 创建一个新的切片,包含config.MqttServer.Topic和extraTopics的所有元素allTopics := append([]string{}, config.MqttServer.Topic...)allTopics = append(allTopics, extraTopics...)var wg sync.WaitGroupfor _, topic := range allTopics {wg.Add(1)go func(t string) {defer wg.Done()token := client.Subscribe(t, 0, messageHandler)if token.Wait() && token.Error() != nil {log.Fatal(token.Error())}log.Printf("Subscribe: %s\n", t)}(topic)}wg.Wait()
}

失物招领小程序

总共为这个失物招领小程序设计了五个页面:

主页:

是小程序的入口,也是所有功能页面的入口

用户页:

显示用户信息,所有用户都需要在这个页面先登录,校验用户名,学号和手机号。如果没有账号的用户也可以在这里注册登录,登录这里采用了DBAPI来校验数据库的信息,直接在同一个节点做数据库查找和比对,效率高

丢失页:

用户可以在这个页面上传和管理他们的失物,这里只有检测到用户登录了之后才会从后端获取该用户的失物记录,页面启动时渲染的数据来自DBAPI的直接获取,若用户上传失物,失物信息通过MQTT客户端传递,在MQTT服务器接收后MQTT客户端才能收到,然后客户端进一步操作修改数据库,保证数据安全。

寻得页:

找到了失物的用户可以在这个页面更新信息,这里和丢失页面一样,初始的数据渲染来自后端DBAPI的直接调,而当用户寻得物品,他的的消息通过MQTT服务器传递,由我们开发的MQTT客户端接收后,进一步操作修改数据库信息,保证信息安全

总结页:

程序运行历史记录总结,信息直接用DBAPI获取

项目运行

小程序直接从源码运行依赖于微信小程序的开发这工具,直接把wxapp文件夹导入开发者工具即可运行。MQTT客户端已经编译好了exe版本,亦可以交叉编译其他的版本,相关命令:

	go mod init client  go mod tidy  GOOS={$YOUR_SYSTEM} GOARCH={$YOUR_CPU} go build -o {$EXE_FILE_NAME} -ldflags '-w -s' ./*.go  ./{$EXE_FILE_NAME}  

GitHub地址

JJLi0427/MQTT_LostFind_WXAPP: Bulid MQTT connection with all arch plantform through our client. Build a lost and found weapp for BJTU use this client. (github.com)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL:NOT IN与NOT EXISTS不等价

在对SQL语句进行性能优化时,经常用到一个技巧是将IN改写成EXISTS,这是等价改写,并没有什么问题。问题在于,将NOT IN改写成NOT EXISTS时,结果未必一样。 目录 一、举例验证二、三值逻辑简述三、附录:用到的S…

【Flask 系统教程 5】视图进阶

类视图 在 Flask 中,除了使用函数视图外,你还可以使用类视图来处理请求。类视图提供了一种更为结构化和面向对象的方式来编写视图函数,使得代码组织更清晰,并且提供了更多的灵活性和可扩展性。 创建类视图 要创建一个类视图&am…

SVM直观理解

https://tangshusen.me/2018/10/27/SVM/ https://www.bilibili.com/video/BV16T4y1y7qj/?spm_id_from333.337.search-card.all.click&vd_source8272bd48fee17396a4a1746c256ab0ae SVM是什么? 先来看看维基百科上对SVM的定义: 支持向量机(英语:su…

使用Python实现二维码生成工具

二维码的本质是什么? 二维码本质上,就是一段字符串。 我们可以把任意的字符串,制作成一个二维码图片。 生活中使用的二维码,更多的是一个 URL 网址。 需要用到的模块 先看一下Python标准库,貌似没有实现这个功能的…

一文了解python机器学习Sklearn

1.3 安装和配置Sklearn 要使用Sklearn库,首先需要安装Python和相应的库。在本教程中,我们将使用Python 3.x版本。可以使用以下命令安装Sklearn库: pip install scikit-learn安装完成后,可以在Python代码中导入Sklearn库&#xf…

code-server容器webpack的ws无法连接解决方法

TLDR 通过指定client的wsrul去连接ws devServer.client.webSocketURL ‘wss://<Forwarded uri>/ws’ 拓扑 1、code-server: 用于编写代码、启动webpack dev-server 服务&#xff1b;[https://<domain>:8001] 2、webpack: 用于浏览dev-server服务&#xff1b;[ht…

在视频中使用时间卷积和半监督训练进行三维人体姿态估计

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;在视频中使用时间卷积和半监督训练进行三维人体姿态估计1、文献摘要2、提出方法2.1、时间扩张卷积模型2.2、半监督方法2.3、与传统…

UE4 Widget制作搜索框

效果&#xff1a; 一、控件层级结构 1.父控件层级结构 2.子控件层级结构 二、蓝图 1.先清除掉创建子项&#xff08;注意&#xff1a;这里使用的是reverse循环&#xff01;&#xff09; 2.判断是否含有关键字&#xff0c;创建子控件

【Android学习】日期和时间选择对话框

实现功能 实现日期和时间选择的对话框&#xff0c;具体效果可看下图(以日期为例) 具体代码 1 日期对话框 1.1 xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android&quo…

AGI要闻:斯坦福李飞飞首次创业,瞄准“空间智能”;OpenAI下周发布搜索产品挑战谷歌;新的开源 AI 眼镜来了|钛媒体AGI | 最新快讯

多方消息证实&#xff0c;OpenAI将会在北京时间5月10日&#xff08;周五&#xff09;凌晨2点公布搜索引擎新产品消息。 斯坦福大学首位红杉讲席教授 李飞飞 通用人工智能&#xff08;AGI&#xff09;领域又公布了一系列重磅消息。 5月4日凌晨&#xff0c;据路透社&#xff0c…

【深度学习】位置编码

一、引言 Self-Attention并行的计算方式未考虑输入特征间的位置关系&#xff0c;这对NLP来说是不可接受的&#xff0c;毕竟一个句子中每个单词都有着明显的顺序关系。Transformer没有RNN、LSTM那样的顺序结构&#xff0c;所以Transformer在提出Self-Attention的同时提出了Posi…

H.265 与 H.264 的主要区别

H.265 与 H.264 的主要区别 H.265 与 H.264 的主要区别各模块技术差异汇总宏块划分帧内预测模式帧间预测模式去块滤波ALF自适应环路滤波采样点自适应偏移&#xff08;Sample Adaptive Offset&#xff09;滤波并行化设计TileEntropy sliceDependent SliceWPP&#xff08;Wavefro…

双fifo流水线操作——verilog练习与设计

文章目录 一、案例分析二、fifo_ctrl模块设计2.1 波形设计&#xff1a;2.2 代码实现2.2.1 fifo_ctrl2.2.2 顶层文件top_fifo_ctrl&#xff08;rx和tx模块省略&#xff09;2.2.3 仿真文件tb_fifo_ctrl 2.3波形仿真 一、案例分析 案例要求&#xff1a;写一个 fifo 控制器&#x…

SPARC VScode EIDE GDB 使用配置

前言 搞了多年的SPARC 最近接触了VSCODE插件感觉好用。想想看不是能方便调试和编译SPARC&#xff0c;决定使用开源的SPARC仿真环境和编译器来试试。感觉的却不错&#xff0c;借此献给使用SPARC的朋友们。安装 1.找微软官方的下载VSCODE. 2.电机左边的方块形状的图标&#xff0…

【强训笔记】day8

NO.3 思路&#xff1a;相乘除以最大公约数等于最小公倍数。最小公倍数等于gcd&#xff08;a&#xff0c;a%b&#xff09;递归直到b等于0。 代码实现&#xff1a; #include <iostream> using namespace std;int gcd(int a,int b) {if(b0) return a;return gcd(b,a%b); }…

二叉树的迭代遍历 | LeetCode 144. 二叉树的前序遍历、LeetCode 94. 二叉树的中序遍历、LeetCode 145. 二叉树的后序遍历

二叉树的前序遍历&#xff08;迭代法&#xff09; 1、题目 题目链接&#xff1a;144. 二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#x…

【北京仁爱堂】事出有因,原来是“肝”出现问题,才导致了痉挛性斜颈

痉挛性斜颈是肌张力障碍疾病中的一种&#xff0c;局限于颈部肌肉。由于颈部肌肉间断或持续的不自主的收缩&#xff0c;导致头颈部扭曲、歪斜、姿势异常。一般在30&#xff5e;40岁发病。由于痉挛性斜颈病因不明&#xff0c;西医方面药物及手术的临床疗效不甚理想&#xff0c;而…

PHP 反序列化

一、PHP 序列化 1、对象的序列化 <?php class people{public $nameGaming;private $NationLiyue;protected $Birthday12/22;public function say(){echo "老板你好呀&#xff0c;我是和记厅的镖师&#xff0c;叫我嘉明就行&#xff0c;要运货吗你&#xff1f;"…

Linux查看某一个程序的安装路径

前提 这一方法的前提条件是&#xff1a;必须是运行着的程序。 方法 这里以查找运行的nginx的安装目录为例。 查看nginx运行进程&#xff0c;查看当前进程的PID&#xff0c;例子中的PID就是7992。 nginps -aux|grep nginx执行ls -l /proc/进程号/exe&#xff0c;然后会打印…

android zygote进程启动流程

一&#xff0c;启动入口 app_main.cpp int main(int argc, char* const argv[]) {if (!LOG_NDEBUG) {String8 argv_String;for (int i 0; i < argc; i) {argv_String.append("\"");argv_String.append(argv[i]);argv_String.append("\" ")…