UDP
延迟低,传输快,传输会有一定的损失。采用广播的形式,一般在局域网中使用。
TCP
latency大概是UDP的两倍,传输信息损失低,端到端。
MQTT通信
概述
MQTT(Message Queuing Telemetyr Transport消息队列遥测传输)基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,该协议构建于TCP/IP协议之上,属于应用层协议。
是一种开放式OASIS和ISO标准的轻量级发布订阅网络协议,可在设备之间传输消息,是当下使用广泛的物联网通信协议之一。
概念
- 角色
- 代理端(Broker)
- 客户端(Client)
- 发布/订阅
- 发布:clinet推向服务器
- 订阅:服务器向clinet推送
支持多客户端采用**发布/订阅(subscribe/publish)**形式进行通信, 代理端负责信息中转:
- 主题(Topic)
用于消息分类,是一个UTF-8字符串
可进行分级
-
服务质量(QOS)
服务质量是为不同应用程序,用户或数据流提供的不同优先级的能力: -
Qos0. 最多一次传送 (只负责传送,发送过后就不管数据的传送情况)
-
Qos1. 至少一次传送 (确认数据交付)
-
Qos2. 正好一次传送 (保证数据交付成功)
优势
采用代理通信的方式, 解耦了发布消息的客户(发布者)与订阅消息的客户(订阅者)之间的关系 -
发布者、订阅者不必了解彼此,只需认同一个代理
-
发布者、订阅者不需要交互,无须等待消息确认
-
发布者、订阅者不要要同时在线,可自由选择时间消费消息
消息格式
每条MQTT命令消息的消息头都包含一个固定的报头,有些消息会携带一个可变报文头和一个负荷。消息格式如下:
固定报文头|可变报文头|负荷
固定报文头(Fixed Header)
最少有两个字节:
- 第一个字节包含消息的类型(Message Type)和QoS级别等标志位。
- 第二个字节开始是剩余长度字节,该长度是后面的可变报文头加消息负载的总长度,该字段最多允许四个字节。
剩余长度字段单个字节的最大值为0x7F. 也就是127个字节。MQTT协议规定,单个字节的最高位如果是1,表示后续还有字节存在,第八位起延续位的作用。
由于MQTT协议最多使用四个字节表示剩余长度,并且最后一个字节的最大值只能是0x7F,而不是0xFF。所以能发送的最大消息长度是256MB,而不是512MB。
可变报文头(Variable Header)
主要包含协议名,协议版本,连接标志,心跳间隔时间,连接返回码,主题名等。
有效负荷
实际上可以理解为消息的主体。当MQTT发送的消息类型是CONNECT(连接)、PUBLISH(发布)、SUBSCRIBE(订阅)、SUBACK(订阅确认)、UBSUNSCRIBE(取消订阅)时会带有负荷。
相关名词
会话(Session)
每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。
会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。
订阅(Subscription)
订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。
订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。
客户端在成功建立TCP连接之后,发送CONNECT消息,在得到服务器端授权允许建立彼此连接的CONNACK消息之后,客户端会发送SUBSCRIBE消息,订阅感兴趣的Topic主题列表(至少一个主题)。
订阅的主题名称采用UTF-8编码,然后紧跟着对应的QoS值。
发布(publish)
控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息,MQTT 客户端发送消息请求,发送完成后返回应用程序线程。
比如安卓的推送服务,还有一些即时通信软件如微信等,也是采用的推送技术。
负载(Payload)
消息订阅者所具体接收的内容。
MQTT中文文档: https://mcxiaoke.gitbooks.io/mqtt-cn/content/
MQTT维基百科: https://zh.wikipedia.org/wiki/MQTT
ESP8266社区论坛:https://github.com/esp8266
使用 MQTT + JSON 进行物联网通信 - 知乎 (zhihu.com)
(46条消息) stm32+8266+onenet+mqtt+json.zip_onenetmqttstm32下发命令-硬件开发文档类资源-优快云文库
Pthon + MQTT
千万不能挂梯子!!!!!!
只能说,幸好有有经验的大佬教。不然一开始就给我干沉默了,都不知道要干啥。
持续发送信息的代码:
如何在 Python 中使用 MQTT | EMQ (emqx.com)
import random
import time
from paho.mqtt import client as mqtt_client
broker = 'mqtt.space'
port = 1883
topic = "/python/mqtt"
# generate client ID with pub prefix randomly
client_id = f'python-mqtt-{random.randint(0, 1000)}'
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(client_id)
client.on_connect = on_connect
client.connect(broker, port)
def publish(client):
msg_count = 0
while True:
time.sleep(1)
msg = f"messages: {msg_count}"
result = client.publish(topic, msg)
# result: [0, 1]
status = result[0]
if status == 0:
print(f"Send `{msg}` to topic `{topic}`")
else:
print(f"Failed to send message to topic {topic}")
msg_count += 1
publish(client)
发送JSON数据的代码:
https://blog.youkuaiyun.com/lhh08hasee/article/details/88025436
可视化软件:MQTT Explorer(感觉对我这种菜鸡极其友好)
# -*- coding: utf-8 -*-
# 以下代码在2019年2月28日 python3.6环境下运行通过
import paho.mqtt.client as mqtt
import json
import time
import random
HOST = "mqtt.space"
PORT = 1883
client_id = f'python-mqtt-{random.randint(0, 1000)}' # 没有就不写,此处部分内容用xxx代替原内容,下同
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
client.subscribe("data/receive") # 订阅消息
def on_message(client, userdata, msg):
print("主题:"+msg.topic+" 消息:"+str(msg.payload.decode('utf-8')))
def on_subscribe(client, userdata, mid, granted_qos):
print("On Subscribed: qos = %d" % granted_qos)
def on_disconnect(client, userdata, rc):
if rc != 0:
print("Unexpected disconnection %s" % rc)
data = {
"type":2,
"timestamp": time.time(),
"messageId":"9fcda359-89f5-4933-xxxx",
"command":"xx/recommend",
"data":{
"openId":"xxxx",
"appId":client_id,
"recommendType":"temRecommend"
}
}
param = json.dumps(data)
client = mqtt.Client(client_id)
client.username_pw_set(client_id, client_id)
client.on_connect = on_connect
client.on_message = on_message
client.on_subscribe = on_subscribe
client.on_disconnect = on_disconnect
client.connect(HOST, PORT, 60)
client.publish("data/send", payload=param, qos=0) # 发送消息
client.loop_forever()
print('Finish!!!')
发布/接收客户端
发布客户端pub.py
:
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("Connected with result code: " + str(rc))
def on_message(client, userdata, msg):
print(msg.topic + " " + str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("mqtt.space", 1883, 600) # 600为keepalive的时间间隔
client.publish('fifa', payload='amazing', qos=0)
接受客户端sub.py
:
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print("Connected with result code: " + str(rc))
def on_message(client, userdata, msg):
print(msg.topic + " " + str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("mqtt.space", 1883, 600) # 600为keepalive的时间间隔
client.subscribe('fifa', qos=0)
client.loop_forever() # 保持连接
先运行sub.py
,然后就开始持续监听,别关。另开一个窗口运行pub.py
,然后之前的客户端就能接收到消息了,发一次收一次。
上面的代码,改改就是奇迹:
得到解析的数据
import json
import paho.mqtt.client as mqtt
def Trying(msg):
"进行判断调用"
print('msg',type(msg.payload))
# json对象转化为python的dict
dict=json.loads(msg.payload)
for k,v in dict.items():
print(k,v)
def on_connect(client, userdata, flags, rc):
print("Connected with result code: " + str(rc))
def on_message(client, userdata, msg):
print(msg.topic + " " + str(msg.payload))
Trying(msg)
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect('mqtt.akasaki.space', 1883, 60) # 600为keepalive的时间间隔
client.subscribe('data/send', qos=0)
print('client._on_message',client._on_message)
client.loop_forever() # 保持连接