mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-29 18:23:59 +08:00
refactor: 重构底层代码,抽离conn,调整消息处理器并创建传输层接口。
feature: 支持mqtt非桥接版本。
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
import asyncio
|
||||
import socket
|
||||
import time
|
||||
from typing import Dict, Any, Set
|
||||
from config.logger import setup_logging
|
||||
from core.protocols.mqtt_connection import MQTTConnection
|
||||
from core.transport.mqtt_transport import MQTTTransport, UDPAudioHandler
|
||||
from core.services.connection_service import ConnectionService
|
||||
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
class MQTTServer:
|
||||
"""
|
||||
原生MQTT服务器:直接处理MQTT协议连接
|
||||
集成到xiaozhi-server架构中
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]):
|
||||
self.config = config
|
||||
self.logger = setup_logging()
|
||||
|
||||
# 服务器配置
|
||||
server_config = config.get('mqtt_server', {})
|
||||
self.mqtt_port = server_config.get('port', 1883)
|
||||
self.udp_port = server_config.get('udp_port', self.mqtt_port)
|
||||
self.host = server_config.get('host', '0.0.0.0')
|
||||
self.public_ip = server_config.get('public_ip', 'localhost')
|
||||
|
||||
# 连接管理
|
||||
self.connections: Dict[int, MQTTConnection] = {}
|
||||
self.udp_handlers: Dict[int, UDPAudioHandler] = {}
|
||||
self.connection_id_counter = 0
|
||||
|
||||
# 服务器实例
|
||||
self.mqtt_server = None
|
||||
self.udp_server = None
|
||||
|
||||
# 连接服务
|
||||
self.connection_service = ConnectionService(config)
|
||||
|
||||
# 活跃连接管理
|
||||
self.active_transports: Set[MQTTTransport] = set()
|
||||
|
||||
# 心跳检查
|
||||
self.heartbeat_task = None
|
||||
self.heartbeat_interval = 30 # 30秒检查一次
|
||||
|
||||
async def start(self):
|
||||
"""启动MQTT服务器"""
|
||||
try:
|
||||
# 启动MQTT TCP服务器
|
||||
await self._start_mqtt_server()
|
||||
|
||||
# 启动UDP服务器
|
||||
await self._start_udp_server()
|
||||
|
||||
# 启动心跳检查
|
||||
self.heartbeat_task = asyncio.create_task(self._heartbeat_check())
|
||||
|
||||
logger.info(f"MQTT服务器启动成功: {self.host}:{self.mqtt_port}")
|
||||
logger.info(f"UDP服务器启动成功: {self.host}:{self.udp_port}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"启动MQTT服务器失败: {e}")
|
||||
raise
|
||||
|
||||
async def _start_mqtt_server(self):
|
||||
"""启动MQTT TCP服务器"""
|
||||
self.mqtt_server = await asyncio.start_server(
|
||||
self._handle_mqtt_connection,
|
||||
self.host,
|
||||
self.mqtt_port
|
||||
)
|
||||
|
||||
async def _start_udp_server(self):
|
||||
"""启动UDP服务器"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# 创建UDP socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind((self.host, self.udp_port))
|
||||
sock.setblocking(False)
|
||||
|
||||
# 创建UDP协议处理器
|
||||
transport, protocol = await loop.create_datagram_endpoint(
|
||||
lambda: UDPProtocol(self),
|
||||
sock=sock
|
||||
)
|
||||
|
||||
self.udp_server = (transport, protocol)
|
||||
|
||||
async def _handle_mqtt_connection(self, reader, writer):
|
||||
"""处理新的MQTT连接"""
|
||||
connection_id = self._generate_connection_id()
|
||||
|
||||
try:
|
||||
# 获取客户端地址
|
||||
client_addr = writer.get_extra_info('peername')
|
||||
logger.info(f"新MQTT连接: {client_addr}, connection_id: {connection_id}")
|
||||
|
||||
# 创建MQTT连接处理器
|
||||
mqtt_connection = MQTTConnection(
|
||||
writer.get_extra_info('socket'),
|
||||
connection_id,
|
||||
self
|
||||
)
|
||||
|
||||
# 创建UDP音频处理器
|
||||
udp_handler = UDPAudioHandler(
|
||||
connection_id,
|
||||
self,
|
||||
{} # 加密配置将在hello消息中设置
|
||||
)
|
||||
|
||||
# 创建MQTT传输层
|
||||
transport = MQTTTransport(mqtt_connection, udp_handler)
|
||||
|
||||
# 注册连接
|
||||
self.connections[connection_id] = mqtt_connection
|
||||
self.udp_handlers[connection_id] = udp_handler
|
||||
self.active_transports.add(transport)
|
||||
|
||||
# 提取连接头信息
|
||||
headers = {
|
||||
'x-real-ip': client_addr[0] if client_addr else 'unknown',
|
||||
'connection-type': 'mqtt'
|
||||
}
|
||||
|
||||
try:
|
||||
# 使用ConnectionService处理连接
|
||||
await self.connection_service.handle_connection(transport, headers)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"ConnectionService处理MQTT连接失败: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理MQTT连接失败: {e}")
|
||||
finally:
|
||||
# 清理连接
|
||||
await self._cleanup_connection(connection_id)
|
||||
|
||||
async def _cleanup_connection(self, connection_id: int):
|
||||
"""清理连接资源"""
|
||||
try:
|
||||
# 移除连接
|
||||
if connection_id in self.connections:
|
||||
connection = self.connections.pop(connection_id)
|
||||
await connection.close()
|
||||
|
||||
# 移除UDP处理器
|
||||
if connection_id in self.udp_handlers:
|
||||
udp_handler = self.udp_handlers.pop(connection_id)
|
||||
await udp_handler.close()
|
||||
|
||||
# 移除传输层(通过连接ID查找)
|
||||
transports_to_remove = []
|
||||
for transport in self.active_transports:
|
||||
if hasattr(transport, '_mqtt_connection') and \
|
||||
transport._mqtt_connection.connection_id == connection_id:
|
||||
transports_to_remove.append(transport)
|
||||
|
||||
for transport in transports_to_remove:
|
||||
self.active_transports.discard(transport)
|
||||
await transport.close()
|
||||
|
||||
logger.info(f"MQTT连接清理完成: {connection_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"清理MQTT连接失败: {e}")
|
||||
|
||||
def _generate_connection_id(self) -> int:
|
||||
"""生成连接ID"""
|
||||
self.connection_id_counter += 1
|
||||
return self.connection_id_counter
|
||||
|
||||
async def on_client_connected(self, mqtt_connection: MQTTConnection):
|
||||
"""客户端连接回调"""
|
||||
logger.info(f"MQTT客户端已连接: {mqtt_connection.client_id}")
|
||||
|
||||
async def on_client_disconnected(self, mqtt_connection: MQTTConnection):
|
||||
"""客户端断开连接回调"""
|
||||
logger.info(f"MQTT客户端已断开: {mqtt_connection.client_id}")
|
||||
|
||||
async def send_udp_message(self, data: bytes, remote_addr: tuple):
|
||||
"""发送UDP消息"""
|
||||
if self.udp_server:
|
||||
transport, protocol = self.udp_server
|
||||
transport.sendto(data, remote_addr)
|
||||
|
||||
async def send_encrypted_audio(self, connection_id: int, audio_data: bytes,
|
||||
timestamp: int, remote_addr: tuple, encryption_config: Dict[str, Any]):
|
||||
"""发送加密音频数据"""
|
||||
try:
|
||||
# 这里应该实现音频数据加密逻辑
|
||||
# 暂时直接发送原始数据
|
||||
header = self._generate_udp_header(connection_id, len(audio_data), timestamp, 0)
|
||||
message = header + audio_data
|
||||
|
||||
await self.send_udp_message(message, remote_addr)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"发送加密音频失败: {e}")
|
||||
|
||||
def _generate_udp_header(self, connection_id: int, length: int, timestamp: int, sequence: int) -> bytes:
|
||||
"""生成UDP消息头"""
|
||||
header = bytearray(16)
|
||||
header[0] = 1 # type
|
||||
header[2:4] = length.to_bytes(2, 'big') # payload length
|
||||
header[4:8] = connection_id.to_bytes(4, 'big') # connection id
|
||||
header[8:12] = timestamp.to_bytes(4, 'big') # timestamp
|
||||
header[12:16] = sequence.to_bytes(4, 'big') # sequence
|
||||
return bytes(header)
|
||||
|
||||
async def _heartbeat_check(self):
|
||||
"""心跳检查任务"""
|
||||
try:
|
||||
while True:
|
||||
await asyncio.sleep(self.heartbeat_interval)
|
||||
|
||||
# 检查所有连接的状态
|
||||
dead_connections = []
|
||||
for connection_id, connection in self.connections.items():
|
||||
if not connection.is_connected():
|
||||
dead_connections.append(connection_id)
|
||||
|
||||
# 清理死连接
|
||||
for connection_id in dead_connections:
|
||||
logger.info(f"清理死连接: {connection_id}")
|
||||
await self._cleanup_connection(connection_id)
|
||||
|
||||
# 记录活跃连接数
|
||||
active_count = len(self.connections)
|
||||
if active_count > 0:
|
||||
logger.info(f"MQTT活跃连接数: {active_count}")
|
||||
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"心跳检查任务出错: {e}")
|
||||
|
||||
async def stop(self):
|
||||
"""停止MQTT服务器"""
|
||||
logger.info("正在停止MQTT服务器...")
|
||||
|
||||
# 停止心跳检查
|
||||
if self.heartbeat_task and not self.heartbeat_task.done():
|
||||
self.heartbeat_task.cancel()
|
||||
try:
|
||||
await self.heartbeat_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# 关闭所有连接
|
||||
for connection_id in list(self.connections.keys()):
|
||||
await self._cleanup_connection(connection_id)
|
||||
|
||||
# 关闭UDP服务器
|
||||
if self.udp_server:
|
||||
transport, protocol = self.udp_server
|
||||
transport.close()
|
||||
|
||||
# 关闭MQTT服务器
|
||||
if self.mqtt_server:
|
||||
self.mqtt_server.close()
|
||||
await self.mqtt_server.wait_closed()
|
||||
|
||||
logger.info("MQTT服务器已停止")
|
||||
|
||||
def get_server_status(self) -> Dict[str, Any]:
|
||||
"""获取服务器状态"""
|
||||
return {
|
||||
'type': 'mqtt',
|
||||
'host': self.host,
|
||||
'mqtt_port': self.mqtt_port,
|
||||
'udp_port': self.udp_port,
|
||||
'active_connections': len(self.connections),
|
||||
'active_transports': len(self.active_transports)
|
||||
}
|
||||
|
||||
|
||||
class UDPProtocol(asyncio.DatagramProtocol):
|
||||
"""UDP协议处理器"""
|
||||
|
||||
def __init__(self, mqtt_server: MQTTServer):
|
||||
self.mqtt_server = mqtt_server
|
||||
self.transport = None
|
||||
|
||||
def connection_made(self, transport):
|
||||
self.transport = transport
|
||||
|
||||
def datagram_received(self, data: bytes, addr: tuple):
|
||||
"""接收UDP数据报"""
|
||||
try:
|
||||
# 解析UDP消息头
|
||||
if len(data) < 16:
|
||||
return
|
||||
|
||||
connection_id = int.from_bytes(data[4:8], 'big')
|
||||
timestamp = int.from_bytes(data[8:12], 'big')
|
||||
sequence = int.from_bytes(data[12:16], 'big')
|
||||
payload = data[16:]
|
||||
|
||||
# 找到对应的UDP处理器
|
||||
udp_handler = self.mqtt_server.udp_handlers.get(connection_id)
|
||||
if udp_handler:
|
||||
udp_handler.on_udp_message(payload, timestamp, addr)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理UDP数据报失败: {e}")
|
||||
|
||||
def error_received(self, exc):
|
||||
logger.error(f"UDP协议错误: {exc}")
|
||||
@@ -0,0 +1,318 @@
|
||||
import asyncio
|
||||
from typing import Dict, Any, List, Optional
|
||||
from config.logger import setup_logging
|
||||
from core.websocket_server_new import NewWebSocketServer
|
||||
from core.servers.mqtt_server import MQTTServer
|
||||
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
class MultiProtocolServer:
|
||||
"""
|
||||
多协议服务器管理器:统一管理WebSocket和MQTT服务器
|
||||
提供统一的启动、停止和状态监控接口
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]):
|
||||
self.config = config
|
||||
self.logger = setup_logging()
|
||||
|
||||
# 服务器实例
|
||||
self.servers: Dict[str, Any] = {}
|
||||
self.server_tasks: Dict[str, asyncio.Task] = {}
|
||||
|
||||
# 服务器状态
|
||||
self.is_running = False
|
||||
self.startup_complete = False
|
||||
|
||||
# 初始化服务器
|
||||
self._initialize_servers()
|
||||
|
||||
def _initialize_servers(self):
|
||||
"""初始化所有协议服务器"""
|
||||
try:
|
||||
# 检查配置中启用的协议
|
||||
enabled_protocols = self.config.get('enabled_protocols', ['websocket'])
|
||||
|
||||
# 初始化WebSocket服务器
|
||||
if 'websocket' in enabled_protocols:
|
||||
self.servers['websocket'] = NewWebSocketServer(self.config)
|
||||
logger.info("WebSocket服务器已初始化")
|
||||
|
||||
# 初始化MQTT服务器
|
||||
if 'mqtt' in enabled_protocols:
|
||||
self.servers['mqtt'] = MQTTServer(self.config)
|
||||
logger.info("MQTT服务器已初始化")
|
||||
|
||||
if not self.servers:
|
||||
logger.warning("没有启用任何协议服务器")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"初始化服务器失败: {e}")
|
||||
raise
|
||||
|
||||
async def start(self):
|
||||
"""启动所有服务器"""
|
||||
if self.is_running:
|
||||
logger.warning("服务器已经在运行中")
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info("开始启动多协议服务器...")
|
||||
self.is_running = True
|
||||
|
||||
# 启动所有服务器
|
||||
for protocol, server in self.servers.items():
|
||||
try:
|
||||
logger.info(f"启动{protocol}服务器...")
|
||||
task = asyncio.create_task(server.start())
|
||||
self.server_tasks[protocol] = task
|
||||
|
||||
# 等待一小段时间确保服务器启动
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
logger.info(f"{protocol}服务器启动成功")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"启动{protocol}服务器失败: {e}")
|
||||
# 继续启动其他服务器
|
||||
continue
|
||||
|
||||
self.startup_complete = True
|
||||
logger.info("多协议服务器启动完成")
|
||||
|
||||
# 启动监控任务
|
||||
asyncio.create_task(self._monitor_servers())
|
||||
|
||||
# 等待所有服务器任务
|
||||
if self.server_tasks:
|
||||
await asyncio.gather(*self.server_tasks.values(), return_exceptions=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"启动多协议服务器失败: {e}")
|
||||
await self.stop()
|
||||
raise
|
||||
|
||||
async def stop(self):
|
||||
"""停止所有服务器"""
|
||||
if not self.is_running:
|
||||
return
|
||||
|
||||
logger.info("开始停止多协议服务器...")
|
||||
self.is_running = False
|
||||
|
||||
# 停止所有服务器
|
||||
for protocol, server in self.servers.items():
|
||||
try:
|
||||
logger.info(f"停止{protocol}服务器...")
|
||||
await server.stop()
|
||||
logger.info(f"{protocol}服务器已停止")
|
||||
except Exception as e:
|
||||
logger.error(f"停止{protocol}服务器失败: {e}")
|
||||
|
||||
# 取消所有服务器任务
|
||||
for protocol, task in self.server_tasks.items():
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
self.server_tasks.clear()
|
||||
logger.info("多协议服务器已停止")
|
||||
|
||||
async def restart(self):
|
||||
"""重启所有服务器"""
|
||||
logger.info("重启多协议服务器...")
|
||||
await self.stop()
|
||||
await asyncio.sleep(1) # 等待清理完成
|
||||
await self.start()
|
||||
|
||||
async def restart_server(self, protocol: str):
|
||||
"""重启指定协议的服务器"""
|
||||
if protocol not in self.servers:
|
||||
logger.error(f"未找到协议服务器: {protocol}")
|
||||
return False
|
||||
|
||||
try:
|
||||
logger.info(f"重启{protocol}服务器...")
|
||||
|
||||
# 停止指定服务器
|
||||
server = self.servers[protocol]
|
||||
await server.stop()
|
||||
|
||||
# 取消任务
|
||||
if protocol in self.server_tasks:
|
||||
task = self.server_tasks[protocol]
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# 重新启动
|
||||
task = asyncio.create_task(server.start())
|
||||
self.server_tasks[protocol] = task
|
||||
|
||||
logger.info(f"{protocol}服务器重启成功")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"重启{protocol}服务器失败: {e}")
|
||||
return False
|
||||
|
||||
async def update_config(self, new_config: Dict[str, Any]):
|
||||
"""更新配置"""
|
||||
try:
|
||||
logger.info("更新多协议服务器配置...")
|
||||
|
||||
# 检查配置变化
|
||||
config_changed = self._check_config_changes(self.config, new_config)
|
||||
|
||||
# 更新配置
|
||||
self.config = new_config
|
||||
|
||||
# 如果配置有重大变化,重新初始化服务器
|
||||
if config_changed:
|
||||
logger.info("配置有重大变化,重新初始化服务器...")
|
||||
await self.stop()
|
||||
self._initialize_servers()
|
||||
if self.is_running:
|
||||
await self.start()
|
||||
else:
|
||||
# 更新各个服务器的配置
|
||||
for protocol, server in self.servers.items():
|
||||
if hasattr(server, 'update_config'):
|
||||
await server.update_config(new_config)
|
||||
|
||||
logger.info("配置更新完成")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"更新配置失败: {e}")
|
||||
return False
|
||||
|
||||
def _check_config_changes(self, old_config: Dict[str, Any], new_config: Dict[str, Any]) -> bool:
|
||||
"""检查配置是否有重大变化"""
|
||||
# 检查启用的协议是否变化
|
||||
old_protocols = set(old_config.get('enabled_protocols', ['websocket']))
|
||||
new_protocols = set(new_config.get('enabled_protocols', ['websocket']))
|
||||
|
||||
if old_protocols != new_protocols:
|
||||
logger.info(f"启用协议发生变化: {old_protocols} -> {new_protocols}")
|
||||
return True
|
||||
|
||||
# 检查服务器端口配置
|
||||
server_configs = ['server', 'mqtt_server']
|
||||
for config_key in server_configs:
|
||||
old_server_config = old_config.get(config_key, {})
|
||||
new_server_config = new_config.get(config_key, {})
|
||||
|
||||
# 检查端口和主机配置
|
||||
for key in ['port', 'host', 'ip']:
|
||||
if old_server_config.get(key) != new_server_config.get(key):
|
||||
logger.info(f"服务器配置{config_key}.{key}发生变化")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def _monitor_servers(self):
|
||||
"""监控服务器状态"""
|
||||
try:
|
||||
while self.is_running:
|
||||
await asyncio.sleep(30) # 每30秒检查一次
|
||||
|
||||
# 检查服务器任务状态
|
||||
for protocol, task in self.server_tasks.items():
|
||||
if task.done():
|
||||
exception = task.exception()
|
||||
if exception:
|
||||
logger.error(f"{protocol}服务器异常退出: {exception}")
|
||||
# 尝试重启服务器
|
||||
await self.restart_server(protocol)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"服务器监控任务出错: {e}")
|
||||
|
||||
def get_server_status(self) -> Dict[str, Any]:
|
||||
"""获取所有服务器状态"""
|
||||
status = {
|
||||
'is_running': self.is_running,
|
||||
'startup_complete': self.startup_complete,
|
||||
'enabled_protocols': list(self.servers.keys()),
|
||||
'servers': {}
|
||||
}
|
||||
|
||||
# 获取各个服务器的状态
|
||||
for protocol, server in self.servers.items():
|
||||
try:
|
||||
if hasattr(server, 'get_server_status'):
|
||||
server_status = server.get_server_status()
|
||||
else:
|
||||
server_status = {'type': protocol, 'status': 'unknown'}
|
||||
|
||||
# 添加任务状态
|
||||
task = self.server_tasks.get(protocol)
|
||||
if task:
|
||||
server_status['task_status'] = 'running' if not task.done() else 'stopped'
|
||||
if task.done() and task.exception():
|
||||
server_status['task_error'] = str(task.exception())
|
||||
|
||||
status['servers'][protocol] = server_status
|
||||
|
||||
except Exception as e:
|
||||
status['servers'][protocol] = {
|
||||
'type': protocol,
|
||||
'status': 'error',
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
return status
|
||||
|
||||
def get_active_connections_count(self) -> Dict[str, int]:
|
||||
"""获取各协议的活跃连接数"""
|
||||
connections = {}
|
||||
|
||||
for protocol, server in self.servers.items():
|
||||
try:
|
||||
if hasattr(server, 'get_active_connections_count'):
|
||||
connections[protocol] = server.get_active_connections_count()
|
||||
elif hasattr(server, 'connections'):
|
||||
connections[protocol] = len(server.connections)
|
||||
else:
|
||||
connections[protocol] = 0
|
||||
except Exception as e:
|
||||
logger.error(f"获取{protocol}连接数失败: {e}")
|
||||
connections[protocol] = -1
|
||||
|
||||
return connections
|
||||
|
||||
async def broadcast_message(self, message: Dict[str, Any], protocol: Optional[str] = None):
|
||||
"""向所有连接广播消息"""
|
||||
try:
|
||||
if protocol:
|
||||
# 向指定协议广播
|
||||
if protocol in self.servers:
|
||||
server = self.servers[protocol]
|
||||
if hasattr(server, 'broadcast_message'):
|
||||
await server.broadcast_message(message)
|
||||
else:
|
||||
# 向所有协议广播
|
||||
for server in self.servers.values():
|
||||
if hasattr(server, 'broadcast_message'):
|
||||
await server.broadcast_message(message)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"广播消息失败: {e}")
|
||||
|
||||
def get_supported_protocols(self) -> List[str]:
|
||||
"""获取支持的协议列表"""
|
||||
return ['websocket', 'mqtt']
|
||||
|
||||
def is_protocol_enabled(self, protocol: str) -> bool:
|
||||
"""检查协议是否启用"""
|
||||
return protocol in self.servers
|
||||
Reference in New Issue
Block a user