diff --git a/.DS_Store b/.DS_Store index 4c33c4e..9e4cb47 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/main/.DS_Store b/main/.DS_Store new file mode 100644 index 0000000..cb99b92 Binary files /dev/null and b/main/.DS_Store differ diff --git a/main/__pycache__/mysql.cpython-310.pyc b/main/__pycache__/mysql.cpython-310.pyc new file mode 100644 index 0000000..a53ba04 Binary files /dev/null and b/main/__pycache__/mysql.cpython-310.pyc differ diff --git a/main/__pycache__/mysql.cpython-311.pyc b/main/__pycache__/mysql.cpython-311.pyc new file mode 100644 index 0000000..f9bbdcb Binary files /dev/null and b/main/__pycache__/mysql.cpython-311.pyc differ diff --git a/main/client/.DS_Store b/main/client/.DS_Store new file mode 100644 index 0000000..ba23691 Binary files /dev/null and b/main/client/.DS_Store differ diff --git a/main/client/Client.py b/main/client/Client.py new file mode 100644 index 0000000..9d6f607 --- /dev/null +++ b/main/client/Client.py @@ -0,0 +1,313 @@ + +import paho.mqtt.client as mqtt + +from main.config.config import HOST, PORT,publish_topic +from queue import Queue +import datetime +import threading +from threading import Thread, Event +import threading +from queue import Queue +import paho.mqtt.client as mqtt +import time +from abc import abstractmethod + + +class Client:#基类 + def __init__(self, userName=None,userPwd=None): + self.userName = userName + self.userPwd = userPwd + self.lock = threading.Lock() + + self.loopNum = 0 + self.start_evt = None # 这是一个Event对象,用来查看是否验证成功 + # 自动在初始化的时候进行链接 + self.client = mqtt.Client() + self.client.on_connect = self.on_connect + self.client.on_message = self.on_message + self.client.connect(HOST, PORT, 60) + + + def start_loop(self): + # 用线程锁来控制同时仅能一个loop_forever + if self.loopNum == 0: + self.lock.acquire() + print('进程锁加载') + self.loopNum = 1 + self.client._thread_terminate = False + self.client.loop_forever() + + def stop_loop(self): + # 停止这个线程 + if self.loopNum == 1: + self.lock.release() + print('进程锁结束!!') + self.client._thread_terminate = True + self.loopNum = 0 + + @abstractmethod + def on_connect(self, client, userdata, flags, rc): + pass + + @abstractmethod + def on_message(self, client, userdata, msg): + pass + def clientStart(self):#启动进程,使用threading(python自带进程管理库)进行管理 + loopThread = threading.Thread(target=self.start_loop) + loopThread.start() + return loopThread + + + +class Register(Client): + def __init__(self,userName,userPwd): + super().__init__(userName,userPwd) + self.clientStart() + self.publishRegister() + + + def on_connect(self, client, userdata, flags, rc):#链接 + if rc == 0: + print("Connected successfully") + returnRegister=self.userName+"register" + client.subscribe(returnRegister) # 订阅 return 主题 + else: + print("Failed to connect, return code %d\n", rc) + + def on_message(self,client, userdata, msg):#接受数据 + # 规定传入数据均为dict的形式 + + data = eval(msg.payload.decode('utf-8')) + #读取数据 + code=data.get('code') + message=data.get('message') + if code==0: + print(message) + if code==1: + print(message) + return data + + def publishRegister(self): + returnTopic=self.userName+"register" + #数据发送特定格式 + data = {'userName': self.userName, 'userPwd': self.userPwd, 'returnTopic': returnTopic} + # qos1 + self.client.publish(publish_topic["register_topic"], str(data).encode(), 1) + # client.loop() + print('发布信息 ', publish_topic['register_topic'], ' 成功') + +class Login(Client): + def __init__(self,userName,userPwd): + super().__init__(userName,userPwd) + self.clientStart() + self.publishLogin() + + def on_connect(self, client, userdata, flags, rc):#链接 + if rc == 0: + print("Connected successfully") + returnLogin=self.userName+"login" + client.subscribe(returnLogin) # 订阅 return 主题 + else: + print("Failed to connect, return code %d\n", rc) + + def on_message(self,client, userdata, msg):#接受数据 + # 规定传入数据均为dict的形式 + + data = eval(msg.payload.decode('utf-8')) + #读取数据 + code=data.get('code') + message=data.get('message') + if code==0: #表示登入失败 + print(message) + if code==1: #表示登入成功 + print(message) + return data + + def publishLogin(self): + returnTopic=self.userName+"login" + #数据发送特定格式 + data = {'userName': self.userName, 'userPwd': self.userPwd, 'returnTopic': returnTopic} + # qos1 + self.client.publish(publish_topic["login_topic"], str(data).encode(), 1) + # client.loop() + print('发布信息 ', publish_topic['login_topic'], ' 成功') + +###################################### +#xiao +class GetMessage(Client): + def __init__(self, userName): + super().__init__(userName=userName) + self.messages=[] + self.clientStart() + self.publishGetAllMessage() + + def on_connect(self, client, userdata, flags, rc): # 链接 + if rc == 0: + print("Connected successfully") + returnLogin = self.userName + "chatall" + client.subscribe(returnLogin) # 订阅 chatall 主题 + else: + print("Failed to connect, return code %d\n", rc) + + def on_message(self, client, userdata, msg): # 接受数据 + # 规定传入数据均为dict的形式 + + messages = eval(msg.payload.decode('utf-8')) + # 读取数据 + self.messages=messages #存到类中 + print(messages) + + + def publishGetAllMessage(self): + returnTopic = self.userName + "chatall" + # 数据发送特定格式 + data = {'userName': self.userName, 'returnTopic': returnTopic} + # qos1 + self.client.publish("chatall", str(data).encode(), 1) + # client.loop() + print('发布信息 ', "chatall", ' 成功') + +###################################### +#jia yikun +class SendMessage(Client): + def __init__(self, userName, msg): + super().__init__(userName=userName) + self.message=msg + self.clientStart() + self.publishSendMessage() + + def on_connect(self, client, userdata, flags, rc): # 链接 + if rc == 0: + print("Connected successfully") + returnLogin = self.userName + "chatsend" + client.subscribe(returnLogin) # 订阅 chatall 主题 + else: + print("Failed to connect, return code %d\n", rc) + + def on_message(self, client, userdata, msg): # 接受数据 + # 规定传入数据均为dict的形式 + + data = eval(msg.payload.decode('utf-8')) + # 读取数据 + code = data.get('code') + message = data.get('message') + if code == 0: # 表示发送消息失败 + print(message) + if code == 1: # 表示发送消息成功 + print(message) + return data + + + + def publishSendMessage(self): + returnTopic = self.userName + "chatsend" + # 数据发送特定格式 + data = {'userName': self.userName,'message':self.message, 'returnTopic': returnTopic} + # qos1 + self.client.publish("chatsend", str(data).encode(), 1) + # client.loop() + print('发布信息 ', "chatsend", ' 成功') + + + +###################################### +#tao yu +class Like(Client): + def __init__(self,messageId): + super().__init__() + self.clientStart() + self.publish_like(messageId) + + @abstractmethod + def on_connect(self, client, userdata, flags, rc): + pass + + @abstractmethod + def on_message(self, client, userdata, msg): + pass + + def publish_like(client, messageId): + data = {'message_id': messageId} + client.publish(publish_topic['like_topic'], str(data).encode(), 1) + print(f'发布信息到 {publish_topic["like_topic"]} 成功') + + +class SecurityQuestion(Client): + def __init__(self, userName, question=None, answer=None, new_password=None): + super().__init__(userName) + self.question = question + self.answer = answer + self.clientStart() + if userName and question and answer and new_password: + self.reset_password(answer,new_password) + elif userName and question and answer: + self.set_security_question() + else: + self.request_security_question() + + def on_connect(self, client, userdata, flags, rc): + if rc == 0: + print("连接成功") + returnTopic = self.userName + "security" + client.subscribe(returnTopic) + else: + print("连接失败,返回码 %d\n", rc) + + def on_message(self, client, userdata, msg): + data = eval(msg.payload.decode('utf-8')) + print(data) + if data.get('action') == 'set_question': + print(data.get('code')) + elif data.get('action') == 'request_question': + print(f"Security Question: {data.get('question')}") + elif data.get('action') == 'verify_security_answer': + print(data.get('code')) + return data + + def set_security_question(self): + returnTopic = self.userName + "security" + data = { + 'action': 'set_question', + 'userName': self.userName, + 'question': self.question, + 'answer': self.answer, + 'returnTopic': returnTopic + } + self.client.publish('set_security_question', str(data).encode(), 1) + print(f'发布信息到 set_security_question_response 成功') + + def request_security_question(self): + returnTopic = self.userName + "security" + data = { + 'action': 'request_question', + 'userName': self.userName, + 'returnTopic': returnTopic + } + self.client.publish('request_security_question', str(data).encode(), 1) + print(f'发布信息到 request_security_question 成功') + + def reset_password(self, answer, new_password): + returnTopic = self.userName + "security" + data = { + 'action': 'reset_password', + 'userName': self.userName, + 'answer': answer, + 'newPassword': new_password, + 'returnTopic': returnTopic + } + self.client.publish('verify_security_answer', str(data).encode(), 1) + print(f'发布信息到 verify_security_answer 成功') + + +#################################################################### +def main(): + + + + +main() + + + + + diff --git a/main/config/__pycache__/config.cpython-310.pyc b/main/config/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..32b391d Binary files /dev/null and b/main/config/__pycache__/config.cpython-310.pyc differ diff --git a/main/config/__pycache__/config.cpython-311.pyc b/main/config/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000..9f89627 Binary files /dev/null and b/main/config/__pycache__/config.cpython-311.pyc differ diff --git a/main/config/config.py b/main/config/config.py new file mode 100644 index 0000000..1445111 --- /dev/null +++ b/main/config/config.py @@ -0,0 +1,17 @@ + +#mysql +mysql_config = { + 'db_name': 'mqtt_test', + 'db_user': 'mqtt_test', + 'db_password': '12345678', + 'db_port': 3306, + 'db_host': '101.43.101.59' +} + +#MQTT broker +HOST = "101.43.101.59" +PORT = 1883 + +#topic +publish_topic={'login_topic':'login','like_topic':'like', + 'chat_topic':'chat','register_topic':'register'} \ No newline at end of file diff --git a/main/mysql.py b/main/mysql.py new file mode 100644 index 0000000..c85963c --- /dev/null +++ b/main/mysql.py @@ -0,0 +1,245 @@ +from main.config.config import mysql_config +from peewee import * +import bcrypt +import datetime + + + +#链接数据库 +db = MySQLDatabase(mysql_config['db_name'], user=mysql_config['db_user'], password=mysql_config['db_password'], + host=mysql_config['db_host'], port=mysql_config['db_port']) + +#数据对象 +####################################################### +#基类 +class BaseModel(Model): + class Meta: + database = db + +#用户类 +class UserModel(BaseModel): + id=AutoField() + name = CharField(max_length=64) + password = CharField(max_length=128) + + + class Meta: + db_table = 'user' + + +#信息类 +class ChatModel(BaseModel): + id=AutoField() + message = TextField() + senderId=BigIntegerField() + time = DateTimeField(default=datetime.datetime.now) + class Meta: + db_table = 'chat' + +#点赞类 +class LikeModel(BaseModel): + id = AutoField() + messageId = ForeignKeyField(ChatModel, backref='likes') + count = IntegerField(default=0) + + class Meta: + db_table = 'like' + +class CreateTable: + @staticmethod + def create(): + db.connect() + db.create_tables([UserModel, ChatModel,LikeModel]) + + + +#用户操作 +########################################## +def hash_password(password): + # 生成盐值 + salt = bcrypt.gensalt() + # 生成哈希值 + hashed = bcrypt.hashpw(password.encode('utf-8'), salt) + return hashed + +# 验证密码 +def check_password(hashed, password): + # 验证密码 + return bcrypt.checkpw(password.encode('utf-8'), hashed) + + +#用户信息传递 +class UserManage: + @staticmethod + def chackName(name):#检测用户是否存在,不存在则打印提示并返回None,否则返回UserModel + try: + user = UserModel.get(UserModel.name == name) + return user + except UserModel.DoesNotExist: + print(f"No user found with name: {name}") + return None + except Exception as error: + print(f"An error occurred: {error}") + return None + @staticmethod + def chackPassword(password):#如果密码正确则返回True + # 密码加密 + hashPassword = hash_password(password) + return check_password(hashPassword,password) + + + @staticmethod + def addUser(name, password): + try: + pwd = hash_password(password) + UserModel.insert(name=name, password=pwd).execute() + return True + except Exception as error: + print(error) + return False + + @staticmethod + def verifyUser(name, password): + user = UserManage.chackName(name) + if user is None: + return False + hashed_password_bytes = user.password.encode('utf-8') + return check_password(hashed_password_bytes, password) + +################################################################ +class ChatManage: + + # 获取并返回所有 chat 数据 + @staticmethod + def getAllManage(): + try: + all_chats = ChatModel.select() + # 将数据转换为列表形式 + chat_list = [] + for chat in all_chats: + chat_data = { + 'id': chat.id, + 'message': chat.message, + 'senderId': chat.senderId, + 'time': chat.time + } + chat_list.append(chat_data) + return chat_list + except Exception as error: + print(error) + return None + + @staticmethod + def AddChat(message,senderId): + try: + ChatModel.insert(message=message,senderId=senderId).execute() + return True + except Exception as error: + print(error) + return False + + + +################################################################# +#taoyu: +class LikeModel(BaseModel): + id = AutoField() + messageId = ForeignKeyField(ChatModel, backref='likes') + count = IntegerField(default=0) + + class Meta: + db_table = 'like' + +def display_all_users(): + try: + users = UserModel.select() + user_list = [] + + for user in users: + last_message = ChatModel.select().where(ChatModel.senderId == user.id).order_by( + ChatModel.time.desc()).first() + if last_message: + last_message_time = last_message.time + else: + last_message_time = "No messages found" + + user_data = { + "id": user.id, + "name": user.name, + "last_message_time": last_message_time + } + user_list.append(user_data) + + print(f"Total users: {len(user_list)}") + print("User list:", user_list) + except Exception as error: + print(f"An error occurred while fetching users: {error}") + + + +class LikeManage: + @staticmethod + def like_message(messageId): + try: + like, created = LikeModel.get_or_create(messageId=messageId, defaults={'count': 0}) + if not created: + like.count += 1 + like.save() + return like.count + except Exception as error: + print(f"An error occurred: {error}") + return None + +class SecurityQuestionModel(BaseModel): + user_id = ForeignKeyField(UserModel, backref='security_question') + question = TextField() + answer = CharField(max_length=128) # 存储加密后的答案 + + class Meta: + db_table = 'security_question' + +class SecurityQuestionManage: + + @staticmethod + def set_security_question(user_name, question, answer): + try: + user = UserModel.get(UserModel.name == user_name) + hashed_answer = hash_password(answer) + SecurityQuestionModel.insert(user_id=user.id, question=question, answer=hashed_answer).execute() + return True + except UserModel.DoesNotExist: + print("用户不存在。") + return False + except Exception as error: + print(f"发生错误: {error}") + return False + + @staticmethod + def verify_security_answer(user_name, answer, new_password): + try: + user = UserModel.get(UserModel.name == user_name) + security_question = SecurityQuestionModel.get(SecurityQuestionModel.user_id == user.id) + if bcrypt.checkpw(answer.encode('utf-8'), security_question.answer.encode('utf-8')): + return SecurityQuestionManage.reset_password(user_name, new_password) + except (UserModel.DoesNotExist, SecurityQuestionModel.DoesNotExist): + print("用户或密保问题不存在。") + return False + except Exception as error: + print(f"发生错误: {error}") + return False + + @staticmethod + def reset_password(user_name, new_password): + try: + user = UserModel.get(UserModel.name == user_name) + hashed_password = hash_password(new_password) + user.password = hashed_password + user.save() + return True + except UserModel.DoesNotExist: + print("用户不存在。") + return False + except Exception as error: + print(f"发生错误: {error}") + return False + diff --git a/main/server/Server.py b/main/server/Server.py new file mode 100644 index 0000000..23bbe0b --- /dev/null +++ b/main/server/Server.py @@ -0,0 +1,259 @@ + +import paho.mqtt.client as mqtt + +from main.config.config import HOST, PORT,publish_topic +from queue import Queue +import datetime +import threading +from threading import Thread, Event +import threading +from queue import Queue +import paho.mqtt.client as mqtt +from main.mysql import * +from abc import abstractmethod +import time + +class Server: + def __init__(self): + self.client = mqtt.Client() + self.client.on_connect = self.on_connect + self.client.on_message = self.on_message + self.client.connect(HOST, PORT, 60) + self.lock = threading.Lock() + self.loopNum = 0 + + def start_loop(self): + # 用线程锁来控制同时仅能一个loop_forever + if self.loopNum == 0: + self.lock.acquire() + print('进程锁加载') + self.loopNum = 1 + self.client._thread_terminate = False + self.client.loop_forever() + + def stop_loop(self): + # 停止这个线程 + if self.loopNum == 1: + self.lock.release() + print('进程锁结束!!') + self.client._thread_terminate = True + self.loopNum = 0 + + def serverStart(self):#启动进程,使用threading(python自带进程管理库)进行管理 + loopThread = threading.Thread(target=self.start_loop) + loopThread.start() + return loopThread + + @abstractmethod + def on_connect(self, client, userdata, flags, rc): + if rc == 0: + print("Connected successfully") + client.subscribe('test') # 订阅 login 主题 + else: + print("Failed to connect, return code %d\n", rc) + + @abstractmethod + def on_message(self, client, userdata, msg): + # 规定传入数据均为dict的形式 + data = eval(msg.payload.decode('utf-8')) + print(data) + return data + + + +class Register(Server): + def __init__(self): + super().__init__() + self.serverStart() + def on_connect(self, client, userdata, flags, rc): + if rc == 0: + print("Connected successfully") + client.subscribe(publish_topic['register_topic']) # 订阅 register 主题 + else: + print("Failed to connect, return code %d\n", rc) + def on_message(self, client, userdata, msg): + # 规定传入数据均为dict的形式 + data = eval(msg.payload.decode('utf-8')) + print(data) + userName = data.get('userName') + userPwd = data.get('userPwd') + returnTopic = data.get('returnTopic') + self.register(userName, userPwd, returnTopic) + return data + + def register(self, userName, userPwd, returnTopic): + user = UserManage.chackName(userName) + if user == None: + print("用户不存在,允许注册") + if UserManage.addUser(userName, userPwd): + print("用户添加成功!") + data = {'code': 1, "message": "用户添加成功!"} + self.client.publish(returnTopic, str(data).encode(), 1) + else: + print("用户添加失败,错误发生在服务器!") + data = {'code': 0, "message": "用户添加失败,错误发生在服务器!"} + self.client.publish(returnTopic, str(data).encode(), 1) + else: + print("用户存在,不允许注册") + data = {'code': 0, "message": "用户添加失败,用户存在"} + self.client.publish(returnTopic, str(data).encode(), 1) + + +class Login(Server): + def __init__(self): + super().__init__() + self.serverStart() + + + def on_connect(self, client, userdata, flags, rc): + if rc == 0: + print("Connected successfully") + client.subscribe(publish_topic['login_topic']) # 订阅 login 主题 + else: + print("Failed to connect, return code %d\n", rc) + + def on_message(self, client, userdata, msg): + # 规定传入数据均为dict的形式 + data = eval(msg.payload.decode('utf-8')) + print(data) + userName = data.get('userName') + userPwd = data.get('userPwd') + returnTopic = data.get('returnTopic') + self.login(userName, userPwd, returnTopic) + return data + + def login(self, userName, userPwd, returnTopic): + user = UserManage.chackName(userName) + if user == None: + print("账号或密码错误") + data = {'code': 0, 'message':"账号或密码错误"} + self.client.publish(returnTopic, str(data).encode(), 1) + else: + if UserManage.verifyUser(userName,userPwd): + print(f"{userName}通过验证,欢迎") + data = {'code': 1, "message": f"验证通过,欢迎回来{userName}"} + self.client.publish(returnTopic, str(data).encode(), 1) + else: + print("账号或密码错误") + data = {'code': 0, 'message': "账号或密码错误"} + self.client.publish(returnTopic, str(data).encode(), 1) + + +################################################################### +#taoyu +class Like(Server): + def __init__(self): + super().__init__() + self.serverStart() + + + def on_connect(self, client, userdata, flags, rc): + if rc == 0: + print("Connected successfully") + client.subscribe('like') + client.subscribe('login') # 订阅 login 主题 + else: + print("Failed to connect, return code %d\n", rc) + + + def on_message(self, client, userdata, msg): + # 规定传入数据均为dict的形式 + if msg.topic == 'like': + data = eval(msg.payload.decode('utf-8')) + messageId = data.get('message_id') + returnTopic = data.get('return_topic') + + if not messageId: + print("Invalid message format") + return + + + # 更新点赞数量 + new_count = LikeManage.like_message(messageId) + + if new_count is None: + print(f"Failed to update like count for message ID: {messageId}") + return + + print(f'处理并新的点赞计数成功') + # 此处可以输出最高点赞的消息ID + try: + top_message_id = LikeModel.select().order_by(LikeModel.count.desc()).get() + if top_message_id: + print(f'Top liked message ID: {top_message_id}') + else: + print('No top liked message found.') + except Exception as e: + print(f"Error retrieving top liked message: {e}") +################################################################################### +#jia yikun +class ChatSave(Server): + def __init__(self): + super().__init__() + self.serverStart() + + def on_connect(self,client, userdata, flags, rc): + + if rc==0: + #self.connected=True + print("Connected successfully") + client.subscribe('chatsend') # 订阅 chat 主题 + else: + raise Exception("Failed to connect mqtt server.") + + def on_message(self, client, userdata, msg): + # 接收用户消息 + data = eval(msg.payload.decode('utf-8')) + message = data.get('message') + userName = data.get('userName') + returnTopic = data.get('returnTopic') + user=UserManage.chackName(userName) + if user == None: + data = {'code': 0, 'message': "消息发送失败,找不到用户"} + self.client.publish(returnTopic, str(data).encode(), 1) + else: + print('new_message ',user.id,':', message) + chat = ChatManage() + if chat.AddChat(message, user.id): + data = {'code': 1, 'message': "消息发送成功"} + self.client.publish(returnTopic, str(data).encode(), 1) + else: + data = {'code': 0, 'message': "消息发送失败"} + self.client.publish(returnTopic, str(data).encode(), 1) + +class ChatAll(Server): + def __init__(self): + super().__init__() + self.serverStart() + + def on_connect(self,client, userdata, flags, rc): + + if rc==0: + #self.connected=True + print("Connected successfully") + client.subscribe('chatall') # 订阅 chat 主题 + else: + raise Exception("Failed to connect mqtt server.") + + def on_message(self, client, userdata, msg): + # 接收用户消息 + data = eval(msg.payload.decode('utf-8')) + returnTopic = data.get('returnTopic') + chat = ChatManage() + payload = str(chat.getAllManage()) + print('历史消息为:') + print(payload) + return self.client.publish(returnTopic, payload=payload, qos=2, retain=False) + + + + +################################################################################### +def main(): + a=Register() #进程1,处理注册 + b=Login() #进程2,处理登入 + c=Like() #进程3,处理点赞 + d=ChatAll() #进程4,处理所有消息 + e=ChatSave() #进程5,处理消息报存 + +main() \ No newline at end of file diff --git a/main/test.py b/main/test.py new file mode 100644 index 0000000..b7796d4 --- /dev/null +++ b/main/test.py @@ -0,0 +1,30 @@ + + +############################################ +#此为我自己进行功能测试的文件,你们可以随意更改,随意提交 +############################################ + +import bcrypt + +# 加密密码 +def hash_password(password): + # 生成盐值 + salt = bcrypt.gensalt() + # 生成哈希值 + hashed = bcrypt.hashpw(password.encode('utf-8'), salt) + return hashed + +# 验证密码 +def check_password(hashed, password): + # 验证密码 + return bcrypt.checkpw(password.encode('utf-8'), hashed) + +# 示例使用 +if __name__ == "__main__": + password = "my_secure_password" + hashed_password = hash_password(password) + print(f"Hashed password: {hashed_password}") + + # 验证密码 + is_correct = check_password(hashed_password, "my_secur1e_password") + print(f"Password is correct: {is_correct}") \ No newline at end of file diff --git a/xiao/.DS_Store b/xiao/.DS_Store new file mode 100644 index 0000000..324544c Binary files /dev/null and b/xiao/.DS_Store differ diff --git "a/xiao/MQTT/sendMessage\357\274\210li\357\274\211.html" "b/xiao/MQTT/sendMessage\357\274\210li\357\274\211.html" new file mode 100644 index 0000000..8e3b532 --- /dev/null +++ "b/xiao/MQTT/sendMessage\357\274\210li\357\274\211.html" @@ -0,0 +1,86 @@ + + +
+ + +