config.json
python
{
"mqtt_host": "broker.emqx.io",
"mqtt_port": 1883,
"mqtt_username": "your_username",
"mqtt_password": "your_password",
"publish_topic": "test/topic/publish",
"subscribe_topic": "test/topic/subscribe",
"tcp_port": 54321
}
mqtt_tcp_bridge.py
python
import json
import logging
import socket
import socketserver
import threading
from paho.mqtt import client as mqtt_client
# 配置日志格式
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# 全局变量和锁,用于管理TCP客户端连接
tcp_clients = []
tcp_clients_lock = threading.Lock()
def load_config(config_file='config.json'):
"""加载配置文件"""
try:
with open(config_file, 'r') as f:
config = json.load(f)
logging.info("配置文件加载成功")
return config
except Exception as e:
logging.error(f"加载配置文件失败: {e}")
raise
def on_mqtt_connect(client, userdata, flags, rc):
"""MQTT连接回调"""
if rc == 0:
logging.info("MQTT连接成功")
# 订阅配置中的主题
client.subscribe(userdata['subscribe_topic'])
else:
logging.error(f"MQTT连接失败,错误码:{rc}")
def on_mqtt_message(client, userdata, msg):
"""MQTT消息接收回调"""
try:
data = msg.payload.decode()
logging.info(f"收到订阅消息: {data} (主题: {msg.topic})")
# 将消息转发给所有TCP客户端
with tcp_clients_lock:
for conn in tcp_clients.copy(): # 使用copy避免迭代时修改
try:
conn.sendall(data.encode() + b'\n') # 添加换行符便于客户端读取
except Exception as e:
logging.error(f"发送数据到TCP客户端失败: {e}")
tcp_clients.remove(conn)
except Exception as e:
logging.error(f"处理MQTT消息失败: {e}")
class TCPRequestHandler(socketserver.BaseRequestHandler):
"""TCP请求处理类"""
def handle(self):
# 客户端连接时添加到列表
with tcp_clients_lock:
tcp_clients.append(self.request)
logging.info(f"新的客户端连接: {self.client_address}")
# 获取MQTT客户端实例用于发布消息
mqtt_client = self.server.mqtt_client
publish_topic = self.server.publish_topic
while True:
try:
data = self.request.recv(1024)
if not data:
break # 客户端断开连接
message = data.decode().strip()
logging.info(f"收到客户端数据: {message}")
# 发布到MQTT主题
mqtt_client.publish(publish_topic, message)
except (ConnectionResetError, BrokenPipeError):
break
except Exception as e:
logging.error(f"处理客户端数据错误: {e}")
break
# 客户端断开时移除
with tcp_clients_lock:
if self.request in tcp_clients:
tcp_clients.remove(self.request)
logging.info(f"客户端断开: {self.client_address}")
class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
"""支持多线程的TCP服务器"""
daemon_threads = True # 主程序退出时自动结束线程
allow_reuse_address = True # 允许地址复用
def run_mqtt_client(config, server):
"""运行MQTT客户端"""
client = mqtt_client.Client()
client.user_data_set(config) # 传递配置数据
client.on_connect = on_mqtt_connect
client.on_message = on_mqtt_message
# 设置用户名密码
if config.get('mqtt_username'):
client.username_pw_set(config['mqtt_username'], config['mqtt_password'])
try:
client.connect(config['mqtt_host'], config['mqtt_port'])
server.mqtt_client = client # 将MQTT客户端实例绑定到服务器
client.loop_start()
return client
except Exception as e:
logging.error(f"MQTT连接错误: {e}")
raise
def main():
# 加载配置
config = load_config()
server_config = {
'host': '0.0.0.0',
'port': config['tcp_port'],
'publish_topic': config['publish_topic']
}
# 启动TCP服务器
with ThreadingTCPServer(
(server_config['host'], server_config['port']), TCPRequestHandler
) as server:
server.publish_topic = server_config['publish_topic']
logging.info(f"TCP服务器启动,监听 {server_config['host']}:{server_config['port']}")
# 启动MQTT客户端并绑定到服务器实例
mqtt_client = run_mqtt_client(config, server)
try:
server.serve_forever()
except KeyboardInterrupt:
logging.info("收到中断信号,关闭服务器...")
server.shutdown()
mqtt_client.loop_stop()
logging.info("服务器已关闭")
if __name__ == '__main__':
main()
python
pip install paho-mqtt
python mqtt_tcp_bridge.py