【m】分窗渲染测试没问题
This commit is contained in:
@@ -112,32 +112,26 @@ function broadcastToGroup(connectionId: string, senderWs: WebSocket, message: an
|
||||
* @param ws WebSocket连接实例
|
||||
*/
|
||||
function remove(ws: WebSocket): void {
|
||||
// 获取连接的所有连接ID
|
||||
const connectionIds = clients.get(ws);
|
||||
if (!connectionIds) return;
|
||||
|
||||
// 遍历所有连接ID
|
||||
connectionIds.forEach(connectionId => {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
if (group) {
|
||||
if (group.host === ws) {
|
||||
// host断开连接,通知所有participants房间已关闭
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ type: "disconnect", connectionId: connectionId, reason: "host-left" }));
|
||||
});
|
||||
// 删除整个连接组
|
||||
connectionGroup.delete(connectionId);
|
||||
} else {
|
||||
// participant断开连接,从participants中移除并通知host
|
||||
group.participants.delete(ws);
|
||||
group.host.send(JSON.stringify({ type: "participant-left", connectionId: connectionId }));
|
||||
// 包含participantId,让host能识别是哪个participant离开
|
||||
group.host.send(JSON.stringify({ type: "participant-left", connectionId: connectionId, participantId: (ws as any).participantId }));
|
||||
}
|
||||
}
|
||||
// 记录删除连接ID的日志
|
||||
console.log(`Remove connectionId: ${connectionId}`);
|
||||
});
|
||||
|
||||
// 从客户端映射中删除
|
||||
clients.delete(ws);
|
||||
}
|
||||
|
||||
@@ -149,30 +143,27 @@ function remove(ws: WebSocket): void {
|
||||
*/
|
||||
function onConnect(ws: WebSocket, connectionId: string): void {
|
||||
let polite = true;
|
||||
// 处理私有模式
|
||||
// 为每个WebSocket生成唯一的participantId
|
||||
const participantId = (ws as any).participantId = (ws as any).participantId || `p_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
if (isPrivate) {
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
// 已有host,新连接作为participant加入
|
||||
group.participants.add(ws);
|
||||
console.log(`Participant joined connectionId: ${connectionId}, total participants: ${group.participants.size}`);
|
||||
console.log(`Participant ${participantId} joined connectionId: ${connectionId}, total participants: ${group.participants.size}`);
|
||||
// 通知host有新participant加入
|
||||
group.host.send(JSON.stringify({ type: "participant-joined", connectionId: connectionId, participantId: participantId }));
|
||||
} else {
|
||||
// 第一个连接成为host
|
||||
connectionGroup.set(connectionId, { host: ws, participants: new Set<WebSocket>() });
|
||||
polite = false;
|
||||
console.log(`Host created connectionId: ${connectionId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取或创建连接ID集合
|
||||
const connectionIds = getOrCreateConnectionIds(ws);
|
||||
// 添加连接ID
|
||||
connectionIds.add(connectionId);
|
||||
// 发送连接成功消息(包含角色信息)
|
||||
const role = polite ? 'participant' : 'host';
|
||||
ws.send(JSON.stringify({ type: "connect", connectionId: connectionId, polite: polite, role: role }));
|
||||
//启用心跳包
|
||||
//AddHeartbeat(ws, connectionId);
|
||||
ws.send(JSON.stringify({ type: "connect", connectionId: connectionId, polite: polite, role: role, participantId: participantId }));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,25 +211,35 @@ function onDisconnect(ws: WebSocket, connectionId: string): void {
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onOffer(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId as string;
|
||||
// 创建新的offer
|
||||
const newOffer = new Offer(message.sdp, Date.now(), false);
|
||||
|
||||
// 处理私有模式
|
||||
if (isPrivate) {
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
const senderParticipantId = (ws as any).participantId;
|
||||
const targetParticipantId = message.participantId;
|
||||
if (group.host === ws) {
|
||||
// host发送offer,转发给所有participants
|
||||
// host发送offer给特定participant(多peer模式下按participantId路由)
|
||||
newOffer.polite = true;
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer }));
|
||||
});
|
||||
if (targetParticipantId) {
|
||||
// 路由到指定participant
|
||||
group.participants.forEach(participantWs => {
|
||||
if ((participantWs as any).participantId === targetParticipantId) {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer, participantId: targetParticipantId }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 兼容:无目标时广播给所有participants
|
||||
group.participants.forEach(participantWs => {
|
||||
const pid = (participantWs as any).participantId;
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer, participantId: pid }));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// participant发送offer,转发给host
|
||||
// participant发送offer给host,携带该participant的participantId
|
||||
newOffer.polite = true;
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer }));
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer, participantId: senderParticipantId }));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -264,30 +265,37 @@ function onOffer(ws: WebSocket, message: any): void {
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onAnswer(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId as string;
|
||||
// 获取或创建连接ID集合
|
||||
const connectionIds = getOrCreateConnectionIds(ws);
|
||||
// 添加连接ID
|
||||
connectionIds.add(connectionId);
|
||||
// 创建新的answer
|
||||
const newAnswer = new Answer(message.sdp, Date.now());
|
||||
|
||||
// 检查连接组是否存在
|
||||
if (!connectionGroup.has(connectionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = connectionGroup.get(connectionId);
|
||||
const senderParticipantId = (ws as any).participantId;
|
||||
// 从answer消息中获取目标participantId(host回复时指定)
|
||||
const targetParticipantId = message.participantId;
|
||||
|
||||
if (group.host === ws) {
|
||||
// host发送answer,转发给所有participants(通常host不发送answer)
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer }));
|
||||
});
|
||||
// host发送answer给特定participant
|
||||
if (targetParticipantId) {
|
||||
group.participants.forEach(participantWs => {
|
||||
if ((participantWs as any).participantId === targetParticipantId) {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer, participantId: targetParticipantId }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 兼容:没有targetParticipantId时广播给所有participants
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer }));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// participant发送answer,转发给host
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer }));
|
||||
// participant发送answer给host,携带自己的participantId
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer, participantId: senderParticipantId }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,150 +306,163 @@ function onAnswer(ws: WebSocket, message: any): void {
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onCandidate(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId;
|
||||
// 创建新的candidate
|
||||
const candidate = new Candidate(message.candidate, message.sdpMLineIndex, message.sdpMid, Date.now());
|
||||
const senderParticipantId = (ws as any).participantId;
|
||||
const targetParticipantId = message.participantId;
|
||||
|
||||
// 处理私有模式
|
||||
if (isPrivate) {
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
if (group.host === ws) {
|
||||
// host发送candidate,转发给所有participants
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate }));
|
||||
});
|
||||
// host发送candidate给特定participant
|
||||
if (targetParticipantId) {
|
||||
group.participants.forEach(participantWs => {
|
||||
if ((participantWs as any).participantId === targetParticipantId) {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate, participantId: targetParticipantId }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate }));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// participant发送candidate,转发给host
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate }));
|
||||
// participant发送candidate给host,携带自己的participantId
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate, participantId: senderParticipantId }));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
function onCallConnectionId(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId;
|
||||
const clientId = message.clientId;
|
||||
// 在1对多模式下,通知host有新的呼叫请求
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
if (group.host !== ws) {
|
||||
// participant发起呼叫,通知host
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
|
||||
|
||||
function onCallConnectionId(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId;
|
||||
const clientId = message.clientId;
|
||||
// 在1对多模式下,通知host有新的呼叫请求
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
if (group.host !== ws) {
|
||||
// participant发起呼叫,通知host
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
|
||||
}
|
||||
} else {
|
||||
// 兼容旧的广播方式
|
||||
clients.forEach((_v, k) => {
|
||||
if (k === ws) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 兼容旧的广播方式
|
||||
clients.forEach((_v, k) => {
|
||||
if (k === ws) {
|
||||
return;
|
||||
}
|
||||
if (_v == clientId) {
|
||||
k.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
|
||||
}
|
||||
if (_v == clientId) {
|
||||
k.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理广播消息请求(1对多模式)
|
||||
* @param ws WebSocket连接实例
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onBroadcast(ws: WebSocket, message: any): void {
|
||||
const broadcastMessage = message.message;
|
||||
const targetConnectionId = message.targetConnectionId;
|
||||
|
||||
if (targetConnectionId) {
|
||||
// 向指定连接组广播
|
||||
if (connectionGroup.has(targetConnectionId)) {
|
||||
const group = connectionGroup.get(targetConnectionId);
|
||||
// 向组内所有成员发送消息
|
||||
group.host.send(JSON.stringify({
|
||||
type: "broadcast",
|
||||
message: broadcastMessage,
|
||||
from: "server"
|
||||
}));
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({
|
||||
type: "broadcast",
|
||||
message: broadcastMessage,
|
||||
from: "server"
|
||||
}));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 全局广播:向所有客户端发送消息
|
||||
clients.forEach((_v, k) => {
|
||||
k.send(JSON.stringify({
|
||||
type: "broadcast",
|
||||
message: broadcastMessage,
|
||||
from: "server"
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function AddHeartbeat(ws: WebSocket, connectionId: string) {
|
||||
// 初始化心跳检测
|
||||
(ws as any).lastActivity = Date.now();
|
||||
|
||||
/**
|
||||
* 处理广播消息请求(1对多模式)
|
||||
* @param ws WebSocket连接实例
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onBroadcast(ws: WebSocket, message: any): void {
|
||||
const broadcastMessage = message.message;
|
||||
const targetConnectionId = message.targetConnectionId;
|
||||
// 设置心跳检测定时器,每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);
|
||||
//ws.close();
|
||||
onDisconnect(ws, connectionId);
|
||||
} else {
|
||||
// 发送ping消息
|
||||
ws.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: { type: "ping"} }));
|
||||
console.log('WebSocket connection heartbeat, lastActivity: ', (ws as any).lastActivity);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
if (targetConnectionId) {
|
||||
// 向指定连接组广播
|
||||
if (connectionGroup.has(targetConnectionId)) {
|
||||
const group = connectionGroup.get(targetConnectionId);
|
||||
// 向组内所有成员发送消息
|
||||
group.host.send(JSON.stringify({
|
||||
type: "broadcast",
|
||||
message: broadcastMessage,
|
||||
from: "server"
|
||||
}));
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({
|
||||
type: "broadcast",
|
||||
message: broadcastMessage,
|
||||
from: "server"
|
||||
}));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 全局广播:向所有客户端发送消息
|
||||
clients.forEach((_v, k) => {
|
||||
k.send(JSON.stringify({
|
||||
type: "broadcast",
|
||||
message: broadcastMessage,
|
||||
from: "server"
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
function AddHeartbeat(ws: WebSocket, connectionId: string) {
|
||||
// 初始化心跳检测
|
||||
(ws as any).lastActivity = Date.now();
|
||||
function RemoveHeartbeat(ws: WebSocket) {
|
||||
// 清除心跳检测定时器
|
||||
if ((ws as any).heartbeatTimer) {
|
||||
clearInterval((ws as any).heartbeatTimer);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置心跳检测定时器,每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);
|
||||
//ws.close();
|
||||
onDisconnect(ws, connectionId);
|
||||
} else {
|
||||
// 发送ping消息
|
||||
ws.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: { type: "ping"} }));
|
||||
console.log('WebSocket connection heartbeat, lastActivity: ', (ws as any).lastActivity);
|
||||
}
|
||||
}, 3000);
|
||||
/**
|
||||
* 处理获取所有连接ID的请求
|
||||
* @param ws WebSocket连接实例
|
||||
*/
|
||||
function onGetAllConnectionIds(): string[] {
|
||||
// 获取所有connectionId
|
||||
const connectionIds = Array.from(connectionGroup.keys());
|
||||
return connectionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理chat-message信令(1对多模式)
|
||||
* host的消息转发给所有participants,participant的消息转发给host
|
||||
* @param ws WebSocket连接实例
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onMessage(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId;
|
||||
const chatMessage = message.message;
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
if (group.host === ws) {
|
||||
// host发送消息,转发给所有participants
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: chatMessage }));
|
||||
});
|
||||
} else {
|
||||
// participant发送消息,转发给host
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: chatMessage }));
|
||||
}
|
||||
function RemoveHeartbeat(ws: WebSocket) {
|
||||
// 清除心跳检测定时器
|
||||
if ((ws as any).heartbeatTimer) {
|
||||
clearInterval((ws as any).heartbeatTimer);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 处理获取所有连接ID的请求
|
||||
* @param ws WebSocket连接实例
|
||||
*/
|
||||
function onGetAllConnectionIds(): string[] {
|
||||
// 获取所有connectionId
|
||||
const connectionIds = Array.from(connectionGroup.keys());
|
||||
return connectionIds;
|
||||
}
|
||||
/**
|
||||
* 处理chat-message信令(1对多模式)
|
||||
* host的消息转发给所有participants,participant的消息转发给host
|
||||
* @param ws WebSocket连接实例
|
||||
* @param message 消息数据
|
||||
*/
|
||||
function onMessage(ws: WebSocket, message: any): void {
|
||||
// 获取连接ID
|
||||
const connectionId = message.connectionId;
|
||||
const chatMessage = message.message;
|
||||
if (connectionGroup.has(connectionId)) {
|
||||
const group = connectionGroup.get(connectionId);
|
||||
if (group.host === ws) {
|
||||
// host发送消息,转发给所有participants
|
||||
group.participants.forEach(participantWs => {
|
||||
participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: chatMessage }));
|
||||
});
|
||||
} else {
|
||||
// participant发送消息,转发给host
|
||||
group.host.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: chatMessage }));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 导出WebSocket处理器函数
|
||||
*/
|
||||
export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId, onBroadcast, onGetAllConnectionIds, AddHeartbeat, RemoveHeartbeat, onMessage, isHost, broadcastToGroup, connectionGroup };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出WebSocket处理器函数
|
||||
*/
|
||||
export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId, onBroadcast, onGetAllConnectionIds, AddHeartbeat, RemoveHeartbeat, onMessage, isHost, broadcastToGroup, connectionGroup };
|
||||
|
||||
Reference in New Issue
Block a user