mirror of https://github.com/djteang/OrangeTV.git
238 lines
6.7 KiB
JavaScript
238 lines
6.7 KiB
JavaScript
/**
|
||
* 独立的WebSocket服务器
|
||
* 完全独立于Next.js,避免任何冲突
|
||
*/
|
||
const WebSocket = require('ws');
|
||
|
||
// 存储已连接的用户
|
||
const connectedUsers = new Map();
|
||
|
||
// 创建独立的WebSocket服务器,使用不同的端口
|
||
function createStandaloneWebSocketServer(port = 3001) {
|
||
const wss = new WebSocket.Server({
|
||
port: port,
|
||
perMessageDeflate: false,
|
||
clientTracking: true
|
||
});
|
||
|
||
console.log(`独立WebSocket服务器已启动在端口 ${port}`);
|
||
|
||
// 连接事件处理
|
||
wss.on('connection', (ws, req) => {
|
||
console.log('新的 WebSocket 连接');
|
||
let userId = null;
|
||
|
||
// 设置心跳检测
|
||
ws.isAlive = true;
|
||
ws.on('pong', () => {
|
||
ws.isAlive = true;
|
||
});
|
||
|
||
// 消息处理
|
||
ws.on('message', (data) => {
|
||
try {
|
||
const message = JSON.parse(data.toString());
|
||
handleMessage(ws, message);
|
||
} catch (error) {
|
||
console.error('解析 WebSocket 消息错误:', error);
|
||
ws.send(JSON.stringify({
|
||
type: 'error',
|
||
data: { message: '消息格式无效' },
|
||
timestamp: Date.now()
|
||
}));
|
||
}
|
||
});
|
||
|
||
// 关闭连接处理
|
||
ws.on('close', () => {
|
||
if (userId) {
|
||
connectedUsers.delete(userId);
|
||
// 广播用户离线状态
|
||
broadcastUserStatus(userId, 'offline');
|
||
console.log(`用户 ${userId} 已断开连接`);
|
||
}
|
||
});
|
||
|
||
// 错误处理
|
||
ws.on('error', (error) => {
|
||
console.error(`WebSocket 错误 ${userId ? `(用户: ${userId})` : ''}:`, error);
|
||
});
|
||
|
||
// 消息处理函数
|
||
function handleMessage(ws, message) {
|
||
switch (message.type) {
|
||
case 'ping':
|
||
// 响应客户端的心跳检测
|
||
ws.send(JSON.stringify({
|
||
type: 'pong',
|
||
timestamp: Date.now()
|
||
}));
|
||
break;
|
||
|
||
case 'user_connect':
|
||
userId = message.data.userId;
|
||
connectedUsers.set(userId, ws);
|
||
console.log(`用户 ${userId} 已连接`);
|
||
|
||
// 确认连接成功
|
||
ws.send(JSON.stringify({
|
||
type: 'connection_confirmed',
|
||
data: { userId },
|
||
timestamp: Date.now()
|
||
}));
|
||
|
||
// 广播用户在线状态
|
||
broadcastUserStatus(userId, 'online');
|
||
|
||
// 发送在线用户列表给新连接的用户
|
||
ws.send(JSON.stringify({
|
||
type: 'online_users',
|
||
data: { users: Array.from(connectedUsers.keys()) },
|
||
timestamp: Date.now()
|
||
}));
|
||
break;
|
||
|
||
case 'message':
|
||
// 转发消息给对话参与者
|
||
if (message.data.participants && Array.isArray(message.data.participants)) {
|
||
message.data.participants.forEach(participantId => {
|
||
// 不发送给自己
|
||
if (participantId !== userId && connectedUsers.has(participantId)) {
|
||
const participantWs = connectedUsers.get(participantId);
|
||
if (participantWs && participantWs.readyState === WebSocket.OPEN) {
|
||
participantWs.send(JSON.stringify(message));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
// 兼容旧版本的receiverId方式
|
||
else if (message.data.receiverId && connectedUsers.has(message.data.receiverId)) {
|
||
const receiverWs = connectedUsers.get(message.data.receiverId);
|
||
if (receiverWs && receiverWs.readyState === WebSocket.OPEN) {
|
||
receiverWs.send(JSON.stringify(message));
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'typing':
|
||
// 转发打字状态给目标用户
|
||
if (message.data.receiverId && connectedUsers.has(message.data.receiverId)) {
|
||
const receiverWs = connectedUsers.get(message.data.receiverId);
|
||
if (receiverWs && receiverWs.readyState === WebSocket.OPEN) {
|
||
receiverWs.send(JSON.stringify(message));
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'friend_request':
|
||
// 转发好友申请给目标用户
|
||
const targetUser = message.data.to_user;
|
||
if (targetUser && connectedUsers.has(targetUser)) {
|
||
const targetWs = connectedUsers.get(targetUser);
|
||
if (targetWs && targetWs.readyState === WebSocket.OPEN) {
|
||
targetWs.send(JSON.stringify(message));
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'friend_accepted':
|
||
// 转发好友接受消息给申请发起人
|
||
const fromUser = message.data.from_user;
|
||
if (fromUser && connectedUsers.has(fromUser)) {
|
||
const fromUserWs = connectedUsers.get(fromUser);
|
||
if (fromUserWs && fromUserWs.readyState === WebSocket.OPEN) {
|
||
fromUserWs.send(JSON.stringify(message));
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 心跳检测定时器
|
||
const heartbeatInterval = setInterval(() => {
|
||
let activeConnections = 0;
|
||
wss.clients.forEach((ws) => {
|
||
if (ws.isAlive === false) {
|
||
console.log('检测到无响应的连接,正在终止...');
|
||
return ws.terminate();
|
||
}
|
||
|
||
ws.isAlive = false;
|
||
// 发送ping
|
||
try {
|
||
ws.ping(() => { });
|
||
activeConnections++;
|
||
} catch (error) {
|
||
console.error('发送ping失败:', error);
|
||
}
|
||
});
|
||
|
||
if (activeConnections > 0) {
|
||
console.log(`心跳检测: 活跃连接数: ${activeConnections}`);
|
||
}
|
||
}, 30000);
|
||
|
||
// 关闭服务器时清理定时器
|
||
wss.on('close', () => {
|
||
clearInterval(heartbeatInterval);
|
||
});
|
||
|
||
return wss;
|
||
}
|
||
|
||
// 广播用户状态
|
||
function broadcastUserStatus(userId, status) {
|
||
const statusMessage = {
|
||
type: 'user_status',
|
||
data: { userId, status },
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
connectedUsers.forEach((ws, connectedUserId) => {
|
||
if (connectedUserId !== userId && ws.readyState === WebSocket.OPEN) {
|
||
ws.send(JSON.stringify(statusMessage));
|
||
}
|
||
});
|
||
}
|
||
|
||
// 获取在线用户列表
|
||
function getOnlineUsers() {
|
||
return Array.from(connectedUsers.keys());
|
||
}
|
||
|
||
// 发送消息给特定用户
|
||
function sendMessageToUser(userId, message) {
|
||
const ws = connectedUsers.get(userId);
|
||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||
ws.send(JSON.stringify(message));
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 发送消息给多个用户
|
||
function sendMessageToUsers(userIds, message) {
|
||
let success = false;
|
||
userIds.forEach(userId => {
|
||
if (sendMessageToUser(userId, message)) {
|
||
success = true;
|
||
}
|
||
});
|
||
return success;
|
||
}
|
||
|
||
// 如果直接运行此文件,启动WebSocket服务器
|
||
if (require.main === module) {
|
||
const port = process.env.WS_PORT || 3001;
|
||
createStandaloneWebSocketServer(port);
|
||
}
|
||
|
||
module.exports = {
|
||
createStandaloneWebSocketServer,
|
||
getOnlineUsers,
|
||
sendMessageToUser,
|
||
sendMessageToUsers,
|
||
broadcastUserStatus
|
||
};
|