3. MQTT over TCP桥接
任务说明
本案例实现一个MQTT网关,通过TCP协议连接远程MQTT Broker,将本地MQTT客户端发布的消息转发至远程Broker,同时将远程Broker下发的消息回传至本地客户端。网关作为本地客户端与远程Broker之间的透明桥接层。
环境依赖
需要安装 paho-mqtt 客户端库:
pip install paho-mqtt
完整实现
import paho.mqtt.client as mqtt
import threading
import time
# ========== 配置 ==========
LOCAL_BROKER_HOST = "127.0.0.1"
LOCAL_BROKER_PORT = 1883
REMOTE_BROKER_HOST = "192.168.1.100" # 远程Broker地址
REMOTE_BROKER_PORT = 1883
BRIDGE_TOPIC_PREFIX = "bridge/" # 桥接主题前缀,避免循环转发
# ========== 远程Broker客户端 ==========
remote_client = mqtt.Client(client_id="bridge_remote")
def on_remote_connect(client, userdata, flags, rc):
print(f"[远程] 已连接远程Broker, 结果码: {rc}")
# 订阅远程下发的主题
client.subscribe(f"{BRIDGE_TOPIC_PREFIX}#")
def on_remote_message(client, userdata, msg):
"""远程Broker消息 -> 转发到本地Broker"""
topic = msg.topic[len(BRIDGE_TOPIC_PREFIX):] # 去掉前缀
local_client.publish(topic, msg.payload, msg.qos, retain=msg.retain)
print(f"[远程->本地] topic={topic}, payload={msg.payload.decode()}")
remote_client.on_connect = on_remote_connect
remote_client.on_message = on_remote_message
# ========== 本地Broker客户端 ==========
local_client = mqtt.Client(client_id="bridge_local")
def on_local_connect(client, userdata, flags, rc):
print(f"[本地] 已连接本地Broker, 结果码: {rc}")
# 订阅所有本地消息(排除桥接回环主题)
client.subscribe("#")
def on_local_message(client, userdata, msg):
"""本地Broker消息 -> 转发到远程Broker"""
# 防止循环:不转发来自远程的消息(通过前缀识别)
if msg.topic.startswith(BRIDGE_TOPIC_PREFIX):
return
remote_topic = f"{BRIDGE_TOPIC_PREFIX}{msg.topic}"
remote_client.publish(remote_topic, msg.payload, msg.qos, retain=msg.retain)
print(f"[本地->远程] topic={msg.topic}, payload={msg.payload.decode()}")
local_client.on_connect = on_local_connect
local_client.on_message = on_local_message
# ========== 启动 ==========
def start_bridge():
# 连接远程Broker
remote_client.connect(REMOTE_BROKER_HOST, REMOTE_BROKER_PORT, keepalive=60)
remote_client.loop_start()
# 连接本地Broker
local_client.connect(LOCAL_BROKER_HOST, LOCAL_BROKER_PORT, keepalive=60)
local_client.loop_start()
print("MQTT over TCP 桥接已启动")
print(f"本地: {LOCAL_BROKER_HOST}:{LOCAL_BROKER_PORT}")
print(f"远程: {REMOTE_BROKER_HOST}:{REMOTE_BROKER_PORT}")
print(f"桥接主题前缀: {BRIDGE_TOPIC_PREFIX}")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n正在停止桥接...")
local_client.loop_stop()
remote_client.loop_stop()
local_client.disconnect()
remote_client.disconnect()
if __name__ == "__main__":
start_bridge()
运行与验证
前置条件:本地和远程各有一个MQTT Broker在运行(如Mosquitto)。修改代码中的 REMOTE_BROKER_HOST 为实际远程Broker地址。
启动桥接:
python mqtt_tcp_bridge.py
验证步骤:
- 在远程Broker所在机器上订阅主题
bridge/test:
mosquitto_sub -h 远程IP -t "bridge/test" -v - 在本地Broker上发布消息:
mosquitto_pub -h 127.0.0.1 -t "test" -m "hello remote" - 观察远程订阅端应收到
bridge/test hello remote,同时桥接程序打印[本地->远程] topic=test - 反向验证:在远程发布
bridge/back主题消息,本地订阅back主题应能收到。
通过条件:桥接程序控制台无报错,双向消息均能按上述路径正确转发,且本地发布普通主题不会在本地产生回环重复。
关键说明
- 使用
bridge/前缀区分消息来源方向,避免桥接两端互相订阅导致无限循环转发。 - 本地客户端订阅
#捕获所有本地消息;远程客户端订阅bridge/#接收需转发到本地的消息。 - QoS和retain标志在转发时原样传递,保证消息语义不丢失。
- 本实现为最小可用桥接,未处理断线重连与持久会话;生产环境可在此基础上增加重连逻辑。