2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* WebSocket处理器
|
|
|
|
|
|
* 负责管理WebSocket连接和信令消息处理
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
import Offer from './offer';
|
|
|
|
|
|
import Answer from './answer';
|
|
|
|
|
|
import Candidate from './candidate';
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 是否为私有模式
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
let isPrivate: boolean;
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 客户端连接映射
|
|
|
|
|
|
* 键: WebSocket实例
|
|
|
|
|
|
* 值: 该WebSocket的连接ID集合
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const clients: Map<WebSocket, Set<string>> = new Map<WebSocket, Set<string>>();
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 连接对映射
|
|
|
|
|
|
* 键: connectionId
|
|
|
|
|
|
* 值: [WebSocket实例1, WebSocket实例2]
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionPair: Map<string, [WebSocket, WebSocket]> = new Map<string, [WebSocket, WebSocket]>();
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取或创建WebSocket会话的连接ID集合
|
|
|
|
|
|
* @param session WebSocket会话实例
|
|
|
|
|
|
* @returns 连接ID的Set集合
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function getOrCreateConnectionIds(session: WebSocket): Set<string> {
|
|
|
|
|
|
let connectionIds = null;
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 检查客户端是否已存在
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (!clients.has(session)) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 如果不存在,创建新的连接ID集合
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionIds = new Set<string>();
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 将新的连接ID集合与客户端关联
|
2026-02-27 18:35:40 +08:00
|
|
|
|
clients.set(session, connectionIds);
|
|
|
|
|
|
}
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取客户端的连接ID集合
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionIds = clients.get(session);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 返回连接ID集合
|
2026-02-27 18:35:40 +08:00
|
|
|
|
return connectionIds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 重置处理器状态
|
|
|
|
|
|
* @param mode 通信模式(public或private)
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function reset(mode: string): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 设置是否为私有模式
|
2026-02-27 18:35:40 +08:00
|
|
|
|
isPrivate = mode == "private";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 添加新的WebSocket连接
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function add(ws: WebSocket): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 为新连接创建空的连接ID集合
|
2026-03-04 22:29:10 +08:00
|
|
|
|
const id = new Set<string>();
|
2026-03-04 17:55:55 +08:00
|
|
|
|
clients.set(ws, id);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 记录添加WebSocket连接的日志
|
2026-03-04 17:55:55 +08:00
|
|
|
|
console.log(`Add WebSocket: ${id}`);
|
2026-02-27 18:35:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 移除WebSocket连接
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function remove(ws: WebSocket): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接的所有连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionIds = clients.get(ws);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 遍历所有连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionIds.forEach(connectionId => {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接对
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const pair = connectionPair.get(connectionId);
|
|
|
|
|
|
if (pair) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 找到另一个WebSocket实例
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const otherSessionWs = pair[0] == ws ? pair[1] : pair[0];
|
|
|
|
|
|
if (otherSessionWs) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 向另一个连接发送断开连接消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
otherSessionWs.send(JSON.stringify({ type: "disconnect", connectionId: connectionId }));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 从连接对映射中删除
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionPair.delete(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 记录删除连接ID的日志
|
|
|
|
|
|
console.log(`Remove connectionId: ${connectionId}`);
|
2026-02-27 18:35:40 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 从客户端映射中删除
|
2026-02-27 18:35:40 +08:00
|
|
|
|
clients.delete(ws);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理连接请求
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param connectionId 连接ID
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function onConnect(ws: WebSocket, connectionId: string): void {
|
|
|
|
|
|
let polite = true;
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 处理私有模式
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (isPrivate) {
|
|
|
|
|
|
if (connectionPair.has(connectionId)) {
|
|
|
|
|
|
const pair = connectionPair.get(connectionId);
|
|
|
|
|
|
|
|
|
|
|
|
if (pair[0] != null && pair[1] != null) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 连接ID已被使用
|
2026-02-27 18:35:40 +08:00
|
|
|
|
ws.send(JSON.stringify({ type: "error", message: `${connectionId}: This connection id is already used.` }));
|
|
|
|
|
|
return;
|
|
|
|
|
|
} else if (pair[0] != null) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 找到配对连接
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionPair.set(connectionId, [pair[0], ws]);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 创建新的连接对
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionPair.set(connectionId, [ws, null]);
|
|
|
|
|
|
polite = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取或创建连接ID集合
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionIds = getOrCreateConnectionIds(ws);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 添加连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionIds.add(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 发送连接成功消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
ws.send(JSON.stringify({ type: "connect", connectionId: connectionId, polite: polite }));
|
2026-03-01 23:41:42 +08:00
|
|
|
|
//启用心跳包
|
|
|
|
|
|
//AddHeartbeat(ws, connectionId);
|
2026-02-27 18:35:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理断开连接请求
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param connectionId 连接ID
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function onDisconnect(ws: WebSocket, connectionId: string): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接的连接ID集合
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionIds = clients.get(ws);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 从集合中删除连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionIds.delete(connectionId);
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 处理连接对
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (connectionPair.has(connectionId)) {
|
|
|
|
|
|
const pair = connectionPair.get(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 找到另一个WebSocket实例
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const otherSessionWs = pair[0] == ws ? pair[1] : pair[0];
|
|
|
|
|
|
if (otherSessionWs) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 向另一个连接发送断开连接消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
otherSessionWs.send(JSON.stringify({ type: "disconnect", connectionId: connectionId }));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 从连接对映射中删除
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionPair.delete(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 向当前连接发送断开连接消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
ws.send(JSON.stringify({ type: "disconnect", connectionId: connectionId }));
|
2026-03-01 23:41:42 +08:00
|
|
|
|
//RemoveHeartbeat(ws);
|
2026-03-04 17:55:55 +08:00
|
|
|
|
// 记录断开连接的日志
|
|
|
|
|
|
console.log(`Disconnect connectionId: ${connectionId}`);
|
2026-02-27 18:35:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理offer信令
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param message 消息数据
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function onOffer(ws: WebSocket, message: any): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionId = message.connectionId as string;
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 创建新的offer
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const newOffer = new Offer(message.sdp, Date.now(), false);
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 处理私有模式
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (isPrivate) {
|
|
|
|
|
|
if (connectionPair.has(connectionId)) {
|
|
|
|
|
|
const pair = connectionPair.get(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 找到另一个WebSocket实例
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const otherSessionWs = pair[0] == ws ? pair[1] : pair[0];
|
|
|
|
|
|
if (otherSessionWs) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 设置为polite模式
|
2026-02-27 18:35:40 +08:00
|
|
|
|
newOffer.polite = true;
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 发送offer消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
otherSessionWs.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer }));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 公共模式:创建新的连接对
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionPair.set(connectionId, [ws, null]);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 向所有其他客户端广播offer
|
2026-02-27 18:35:40 +08:00
|
|
|
|
clients.forEach((_v, k) => {
|
|
|
|
|
|
if (k == ws) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
k.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer }));
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理answer信令
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param message 消息数据
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function onAnswer(ws: WebSocket, message: any): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionId = message.connectionId as string;
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取或创建连接ID集合
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionIds = getOrCreateConnectionIds(ws);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 添加连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
connectionIds.add(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 创建新的answer
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const newAnswer = new Answer(message.sdp, Date.now());
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 检查连接对是否存在
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (!connectionPair.has(connectionId)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接对
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const pair = connectionPair.get(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 找到另一个WebSocket实例
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const otherSessionWs = pair[0] == ws ? pair[1] : pair[0];
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 公共模式:更新连接对
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (!isPrivate) {
|
|
|
|
|
|
connectionPair.set(connectionId, [otherSessionWs, ws]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 发送answer消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
otherSessionWs.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer }));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理candidate信令
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param message 消息数据
|
|
|
|
|
|
*/
|
2026-02-27 18:35:40 +08:00
|
|
|
|
function onCandidate(ws: WebSocket, message: any): void {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 获取连接ID
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const connectionId = message.connectionId;
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 创建新的candidate
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const candidate = new Candidate(message.candidate, message.sdpMLineIndex, message.sdpMid, Date.now());
|
|
|
|
|
|
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 处理私有模式
|
2026-02-27 18:35:40 +08:00
|
|
|
|
if (isPrivate) {
|
|
|
|
|
|
if (connectionPair.has(connectionId)) {
|
|
|
|
|
|
const pair = connectionPair.get(connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 找到另一个WebSocket实例
|
2026-02-27 18:35:40 +08:00
|
|
|
|
const otherSessionWs = pair[0] == ws ? pair[1] : pair[0];
|
|
|
|
|
|
if (otherSessionWs) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 发送candidate消息
|
2026-02-27 18:35:40 +08:00
|
|
|
|
otherSessionWs.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate }));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-04 17:55:55 +08:00
|
|
|
|
function onCallConnectionId(ws: WebSocket, message: any): void {
|
|
|
|
|
|
// 获取连接ID
|
|
|
|
|
|
const connectionId = message.connectionId;
|
|
|
|
|
|
const clientId = message.clientId;
|
|
|
|
|
|
clients.forEach((_v, k) => {
|
|
|
|
|
|
if (k === ws) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_v == clientId) {
|
|
|
|
|
|
k.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
|
|
|
|
|
|
}
|
2026-02-27 18:35:40 +08:00
|
|
|
|
|
2026-03-04 17:55:55 +08:00
|
|
|
|
});
|
2026-02-28 18:17:08 +08:00
|
|
|
|
|
2026-03-04 17:55:55 +08:00
|
|
|
|
}
|
2026-02-28 18:17:08 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-03-04 17:55:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理广播消息请求
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param message 消息数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理广播消息请求
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param message 消息数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
function onBroadcast(ws: WebSocket, message: any): void {
|
|
|
|
|
|
const broadcastMessage = message.message;
|
|
|
|
|
|
const targetConnectionId = message.targetConnectionId;
|
|
|
|
|
|
|
|
|
|
|
|
if (targetConnectionId) {
|
|
|
|
|
|
// 向指定连接广播
|
|
|
|
|
|
if (connectionPair.has(targetConnectionId)) {
|
|
|
|
|
|
const pair = connectionPair.get(targetConnectionId);
|
|
|
|
|
|
// 向连接对中的两个WebSocket实例发送消息
|
|
|
|
|
|
if (pair[0]) {
|
|
|
|
|
|
pair[0].send(JSON.stringify({
|
|
|
|
|
|
type: "broadcast",
|
|
|
|
|
|
message: broadcastMessage,
|
|
|
|
|
|
from: "server"
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pair[1]) {
|
|
|
|
|
|
pair[1].send(JSON.stringify({
|
|
|
|
|
|
type: "broadcast",
|
|
|
|
|
|
message: broadcastMessage,
|
|
|
|
|
|
from: "server"
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 全局广播:向所有客户端发送消息
|
|
|
|
|
|
clients.forEach((_v, k) => {
|
|
|
|
|
|
k.send(JSON.stringify({
|
|
|
|
|
|
type: "broadcast",
|
|
|
|
|
|
message: broadcastMessage,
|
|
|
|
|
|
from: "server"
|
|
|
|
|
|
}));
|
|
|
|
|
|
});
|
2026-02-28 18:17:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-04 17:55:55 +08:00
|
|
|
|
function AddHeartbeat(ws: WebSocket, connectionId: string) {
|
|
|
|
|
|
// 初始化心跳检测
|
2026-02-28 18:17:08 +08:00
|
|
|
|
(ws as any).lastActivity = Date.now();
|
|
|
|
|
|
|
|
|
|
|
|
// 设置心跳检测定时器,每30秒发送一次ping
|
|
|
|
|
|
(ws as any).heartbeatTimer = setInterval(() => {
|
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
|
// 检查上次活动时间,如果超过60秒没有活动,关闭连接
|
|
|
|
|
|
if (now - (ws as any).lastActivity > 10000) {
|
|
|
|
|
|
console.log('WebSocket connection timeout, closing...');
|
|
|
|
|
|
clearInterval((ws as any).heartbeatTimer);
|
2026-03-01 23:41:42 +08:00
|
|
|
|
//ws.close();
|
|
|
|
|
|
onDisconnect(ws, connectionId);
|
2026-02-28 18:17:08 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 发送ping消息
|
|
|
|
|
|
ws.send(JSON.stringify({ type: "ping" }));
|
|
|
|
|
|
console.log('WebSocket connection heartbeat, lastActivity: ', (ws as any).lastActivity);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 3000);
|
2026-03-04 17:55:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
function RemoveHeartbeat(ws: WebSocket) {
|
2026-02-28 18:17:08 +08:00
|
|
|
|
// 清除心跳检测定时器
|
|
|
|
|
|
if ((ws as any).heartbeatTimer) {
|
|
|
|
|
|
clearInterval((ws as any).heartbeatTimer);
|
|
|
|
|
|
}
|
2026-03-04 17:55:55 +08:00
|
|
|
|
}
|
2026-03-04 22:29:10 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理获取所有连接ID的请求
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
*/
|
|
|
|
|
|
function onGetAllConnectionIds(): string[] {
|
|
|
|
|
|
// 获取所有connectionId
|
|
|
|
|
|
const connectionIds = Array.from(connectionPair.keys());
|
|
|
|
|
|
// 发送连接ID列表给客户端
|
|
|
|
|
|
// ws.send(JSON.stringify({
|
|
|
|
|
|
// type: "connection-ids",
|
|
|
|
|
|
// connectionIds: connectionIds
|
|
|
|
|
|
// }));
|
|
|
|
|
|
return connectionIds;
|
|
|
|
|
|
}
|
2026-03-10 19:09:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理chat-message信令
|
|
|
|
|
|
* @param ws WebSocket连接实例
|
|
|
|
|
|
* @param message 消息数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
function onChatMessage(ws: WebSocket, message: any): void {
|
|
|
|
|
|
// 获取连接ID
|
|
|
|
|
|
const connectionId = message.connectionId;
|
|
|
|
|
|
const chatMessage = message.message;
|
|
|
|
|
|
if (connectionPair.has(connectionId)) {
|
|
|
|
|
|
const pair = connectionPair.get(connectionId);
|
|
|
|
|
|
// 找到另一个WebSocket实例
|
|
|
|
|
|
const otherSessionWs = pair[0] == ws ? pair[1] : pair[0];
|
|
|
|
|
|
if (otherSessionWs) {
|
|
|
|
|
|
// 发送chat-message消息
|
|
|
|
|
|
otherSessionWs.send(JSON.stringify({ from: connectionId, to: "", type: "chat-message", data: chatMessage }));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-04 17:55:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 导出WebSocket处理器函数
|
|
|
|
|
|
*/
|
2026-03-10 19:09:41 +08:00
|
|
|
|
export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId, onBroadcast, onGetAllConnectionIds, AddHeartbeat, RemoveHeartbeat ,onChatMessage};
|