一、基本原理
1.1 不同协议之间的层级关系
根据我个人的学习路线,我先是学习了传输层面的TCP协议,然后是学习了应用层面的MQTT协议。因此,弄清楚不同协议之间的关系尤为重要
1.2 TCP与MQTT之间的比较
二、使用emqx搭建本地mqtt服务器
2.1 说明
由于emqx官网在4月份停止了对windows的原生态兼容,我所用的版本为在网络上下载的zip包,为了避免后续出现问题,会考虑使用虚拟机或者docker实现本地mqtt服务器的搭建
2.2搭建过程
解压emqx的win版本压缩包,使用命令行安装并运行程序,最后将程序添加到开机自启的文件夹中在浏览器打开服务器的本地配置网址:http://localhost:18083/
至此,本地mqtt服务器搭建完成
三、使用python基于本地mqtt服务器完成基本的数据传输
我在搭建此服务器之前利用python实现了两个简单的基于tcp协议传输数据的服务端和客户端程序,因此,我想在其中服务端的基础上编写mqtt客户端,把接收到的数据通过mqtt协议发送给另外一个客户端(也使用python进行编写,并订阅另一客户端数据)。
参考基础教学文章,使用python的paho-mqtt模块完成基本的消息发布和消息订阅代码
3.1 消息发布
from paho.mqtt import client as mqtt import uuid def on_connect(client, userdata, flags, rc): """ 一旦连接成功, 回调此方法 rc的值表示成功与否: 0:连接成功 1:连接被拒绝-协议版本不正确 2:连接被拒绝-客户端标识符无效 3:连接被拒绝-服务器不可用 4:连接被拒绝-用户名或密码不正确 5:连接被拒绝-未经授权 6-255:当前未使用。 """ rc_status = ["连接成功", "协议版本不正确", "客户端标识符无效", "服务器不可用", "用户名或密码不正确", "未经授权"] print("connect:", rc_status[rc]) def mqtt_connect(): """连接MQTT服务器""" mqttClient = mqtt.Client(str(uuid.uuid4())) mqttClient.on_connect = on_connect # 返回连接状态的回调函数 MQTTHOST = "127.0.0.1" # MQTT服务器地址 MQTTPORT = 1883 # MQTT端口 mqttClient.username_pw_set("admin", "200312ABcd") # MQTT服务器账号密码, 无密码时注释即可 mqttClient.connect(MQTTHOST, MQTTPORT, 60) mqttClient.loop_start() # 启用线程连接 return mqttClient def mqtt_publish(): """发布主题为'mqtt/demo',内容为'Demo text',服务质量为2""" mqttClient = mqtt_connect() text = "Demo text" mqttClient.publish('mqtt/demo', text, 2) mqttClient.loop_stop() if __name__ == '__main__': mqtt_publish()
3.2 消息订阅
from paho.mqtt import client as mqtt import uuid def on_connect(client, userdata, flags, rc): """一旦连接成功, 回调此方法""" rc_status = ["连接成功", "协议版本不正确", "客户端标识符无效", "服务器不可用", "用户名或密码不正确", "未经授权"] print("connect:", rc_status[rc]) def on_message(client, userdata, msg): """一旦订阅到消息, 回调此方法""" print("主题:"+msg.topic+" 消息:"+str(msg.payload.decode('gb2312'))) def mqtt_connect(): """连接MQTT服务器""" mqttClient = mqtt.Client(str(uuid.uuid4())) mqttClient.on_connect = on_connect # 返回连接状态的回调函数 mqttClient.on_message = on_message # 返回订阅消息回调函数 MQTTHOST = "127.0.0.1" # MQTT服务器地址 MQTTPORT = 1883 # MQTT端口 mqttClient.username_pw_set("admin", "200312ABcd") # mqtt服务器账号密码 mqttClient.connect(MQTTHOST, MQTTPORT, 60) mqttClient.loop_start() # 启用线程连接 return mqttClient def on_subscribe(): """订阅主题:mqtt/demo""" mqttClient = mqtt_connect() mqttClient.subscribe("mqtt/demo", 2) while True: pass if __name__ == '__main__': on_subscribe()
四、完整的代码实现
4.1客户端1(基于原tcp通讯服务端)
import pymysql from socket import * from 作图 import init_plot, update_plot import datetime from paho.mqtt import client as mqtt import uuid from 发布信息 import on_connect,mqtt_connect,mqtt_publish IP = '127.0.0.1' PORT = 50000 BUFLEN = 1024 y_data = [] conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',db='test2',charset='utf8') cur=conn.cursor() #cur.execute("create database test2 default charset utf8 collate utf8_general_ci") # cur.execute("use test2") # cur.execute("create table t1(number int,time datetime,ip text)") # conn.commit() count=0 # 初始化图形和线条 fig, ax, line = init_plot() #接受客户端连接 listen_socket = socket(AF_INET, SOCK_STREAM) listen_socket.bind((IP, PORT)) listen_socket.listen(1) print(f"服务端启动成功,在{PORT}端口等待客户端连接") data_socket, addr = listen_socket.accept() print("接受一个客户端连接", addr) #接受数据并且绘制图像 while True: data = data_socket.recv(BUFLEN) if not data: break info = data.decode() print(f"收到客户端数据:{info}") y_data.append(int(info)) if len(y_data) > 20: y_data.pop(0) update_plot(y_data, line) #获取时间 current_time=datetime.datetime.now() current=current_time.strftime('%Y-%m-%d %H:%M:%S') information=f"{current}--{info}" mqtt_publish(information) #写入MySQL数据库 cur.execute(f"insert into t1(number,time,ip) values(%s,%s,%s) ",(int(info),current,IP)) conn.commit() #写入20行 count+=1 if count == 20: break data_socket.close() listen_socket.close()
4.2 客户端2(订阅并接收数据)
from paho.mqtt import client as mqtt import uuid with open('接收数据.txt', 'w') as file: def on_connect(client, userdata, flags, rc): """一旦连接成功, 回调此方法""" rc_status = ["连接成功", "协议版本不正确", "客户端标识符无效", "服务器不可用", "用户名或密码不正确", "未经授权"] print("connect:", rc_status[rc]) def on_message(client, userdata, msg): """一旦订阅到消息, 回调此方法""" s1=str(msg.payload.decode('gb2312')) print("主题:"+msg.topic+" 消息:"+s1) file.write(f'{s1}\n') def mqtt_connect(): """连接MQTT服务器""" mqttClient = mqtt.Client(str(uuid.uuid4())) mqttClient.on_connect = on_connect # 返回连接状态的回调函数 mqttClient.on_message = on_message # 返回订阅消息回调函数 MQTTHOST = "127.0.0.1" # MQTT服务器地址 MQTTPORT = 1883 # MQTT端口 mqttClient.username_pw_set("admin", "200312ABcd") # mqtt服务器账号密码 mqttClient.connect(MQTTHOST, MQTTPORT, 60) mqttClient.loop_start() # 启用线程连接 return mqttClient def on_subscribe(): """订阅主题:mqtt/demo""" mqttClient = mqtt_connect() mqttClient.subscribe("接收到的数据", 2) while True: pass if __name__ == '__main__': on_subscribe()
4.3 客户端(原tcp通讯客户端,每秒发送一个随机数字)
from socket import * import random import time IP = '127.0.0.1' PORT = 50000 BUFLEN = 1024 data_socket=socket(AF_INET,SOCK_STREAM) data_socket.connect((IP,PORT)) print("客户端连接成功") while True: num=random.randint(1,100) s1=str(num) data_socket.send(s1.encode()) time.sleep(1)
五、小结
总的来说,以上代码实现了编写两个mqtt客户端程序。其中一个mqtt客户端在之前tcp服务器程序基础上实现,把接收到的数据通过mqtt发布给另一个mqtt客户端,该客户端订阅到数据后将数据保存到文本文件,保存信息为时间戳和数据。