project: 增加web和adb控制,未测试

This commit is contained in:
2026-07-17 19:17:10 +08:00
parent 5fb7b58fc3
commit b8edd5eff5
49 changed files with 5061 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
// 轻量信令服务器,协议与 WebRTCSignalServer (Spring) 完全兼容。
// 端点: ws://host:PORT/ws/signal
// 支持消息: REGISTER / DEVICE_LIST / OFFER / ANSWER / ICE_CANDIDATE / CONTROL_COMMAND / CONNECTION_REJECTED
// 支持通知: REGISTER_SUCCESS / DEVICE_LIST / TARGET_OFFLINE / REQUEST_ERROR / REQUEST_TIMEOUT
import { WebSocketServer } from 'ws';
const PORT = Number(process.env.PORT || 8088);
const PATH = process.env.WS_PATH || '/ws/signal';
const wss = new WebSocketServer({ port: PORT, path: PATH });
// deviceId -> { ws, type }
const sessions = new Map();
// "from->to" -> true等待被控端确认
const pending = new Map();
const keyOf = (a, b) => `${a}->${b}`;
function send(ws, obj) {
if (ws && ws.readyState === ws.OPEN) ws.send(JSON.stringify(obj));
}
wss.on('connection', (ws) => {
ws.on('message', (data) => {
let msg;
try {
msg = JSON.parse(data.toString());
} catch {
return;
}
const type = (msg.type || '').toUpperCase();
switch (type) {
case 'REGISTER': handleRegister(ws, msg); break;
case 'DEVICE_LIST': handleDeviceList(ws); break;
case 'OFFER': handleOffer(msg); break;
case 'ANSWER':
case 'CONNECTION_REJECTED':
pending.delete(keyOf(msg.toDeviceId, msg.fromDeviceId));
forward(msg);
break;
case 'ICE_CANDIDATE':
case 'CONTROL_COMMAND':
forward(msg);
break;
default:
forward(msg);
}
});
ws.on('close', () => {
for (const [id, s] of sessions) {
if (s.ws === ws) { sessions.delete(id); break; }
}
});
});
function handleRegister(ws, msg) {
const id = msg.fromDeviceId;
const type = (msg.deviceType || '').toUpperCase();
if (!id || !type) return;
sessions.set(id, { ws, type });
send(ws, { type: 'REGISTER_SUCCESS', deviceId: id });
console.log(`设备注册: ${id} (${type})`);
}
function handleDeviceList(ws) {
const controllers = [];
const controlled = [];
for (const [id, s] of sessions) {
if (s.type === 'CONTROLLER') controllers.push(id);
else if (s.type === 'CONTROLLED') controlled.push(id);
}
send(ws, { type: 'DEVICE_LIST', controllers, controlled });
}
function handleOffer(msg) {
const from = msg.fromDeviceId;
const to = msg.toDeviceId;
const fromType = sessions.get(from)?.type;
const toType = sessions.get(to)?.type;
if (fromType !== 'CONTROLLER' || toType !== 'CONTROLLED') {
send(sessions.get(from)?.ws, {
type: 'REQUEST_ERROR',
toDeviceId: to,
payload: '连接请求只能由 CONTROLLER 发往 CONTROLLED',
});
return;
}
const target = sessions.get(to)?.ws;
if (!target) {
send(sessions.get(from)?.ws, {
type: 'TARGET_OFFLINE',
toDeviceId: to,
payload: '目标被控端不在线,请确认设备已开启并连接服务器',
});
return;
}
pending.set(keyOf(from, to), true);
forward(msg);
}
function forward(msg) {
const target = sessions.get(msg.toDeviceId)?.ws;
if (!target) return;
send(target, msg);
}
console.log(`信令服务器已启动: ws://localhost:${PORT}${PATH}`);