translate comment
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: "signaling-server",
|
||||
script: "./dist/server.js", // 指向编译后的文件
|
||||
script: "./dist/server.js", // Point to the compiled file
|
||||
watch: false,
|
||||
env: {
|
||||
"NODE_ENV": "production",
|
||||
@@ -13,6 +13,6 @@ module.exports = {
|
||||
max_memory_restart: "500M",
|
||||
instances: 1,
|
||||
exec_mode: "fork",
|
||||
group: "ssl-cert" // 添加这行,指定进程运行的组
|
||||
group: "ssl-cert" // Add this line to specify the group the process runs as
|
||||
}]
|
||||
}
|
||||
@@ -3,7 +3,7 @@ const Redis = require('ioredis');
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
|
||||
// Redis 客户端配置
|
||||
// Redis client configuration
|
||||
const redis = new Redis({
|
||||
host: 'localhost',
|
||||
port: 6379,
|
||||
@@ -15,16 +15,16 @@ async function exportTrackingData() {
|
||||
const fileName = `tracking-data-${timestamp}.txt`;
|
||||
const filePath = path.join(__dirname, '../logs', fileName);
|
||||
|
||||
// 创建输出内容
|
||||
// Create output content
|
||||
let output = '=== Tracking Data Export ===\n';
|
||||
output += `Generated at: ${new Date().toISOString()}\n\n`;
|
||||
|
||||
// 1. 获取所有来源
|
||||
// 1. Get all sources
|
||||
const sources = await redis.smembers('referrers:sources');
|
||||
output += '=== Referral Sources ===\n';
|
||||
output += sources.join(', ') + '\n\n';
|
||||
|
||||
// 2. 获取总计数
|
||||
// 2. Get total counts
|
||||
const totalCounts = await redis.hgetall('referrers:count');
|
||||
output += '=== Total Counts by Source ===\n';
|
||||
for (const [source, count] of Object.entries(totalCounts)) {
|
||||
@@ -32,21 +32,21 @@ async function exportTrackingData() {
|
||||
}
|
||||
output += '\n';
|
||||
|
||||
// 3. 获取每日统计数据并按日期排序
|
||||
// 3. Get daily statistics and sort by date
|
||||
output += '=== Daily Statistics ===\n';
|
||||
const dailyKeys = await redis.keys('referrers:daily:*');
|
||||
|
||||
// 将 key 转换为包含日期对象的数组,并按日期排序
|
||||
// Convert keys to an array of objects with dates and sort by date
|
||||
const dailyData = await Promise.all(dailyKeys.map(async (key) => {
|
||||
const date = key.split(':')[2];
|
||||
const dailyStats = await redis.hgetall(key);
|
||||
return { date: new Date(date), data: dailyStats };
|
||||
}));
|
||||
|
||||
dailyData.sort((a, b) => b.date.getTime() - a.date.getTime()); // 按日期降序排序(最近到最远)
|
||||
dailyData.sort((a, b) => b.date.getTime() - a.date.getTime()); // Sort by date in descending order (most recent to oldest)
|
||||
|
||||
for (const item of dailyData) {
|
||||
const dateString = item.date.toISOString().split('T')[0]; //日期格式化字符串
|
||||
const dateString = item.date.toISOString().split('T')[0]; // Format date string
|
||||
output += `\nDate: ${dateString}\n`;
|
||||
for (const [source, count] of Object.entries(item.data)) {
|
||||
output += ` ${source}: ${count}\n`;
|
||||
@@ -55,14 +55,14 @@ async function exportTrackingData() {
|
||||
|
||||
output += '\n';
|
||||
|
||||
// 5. 添加基本统计信息
|
||||
// 5. Add basic statistics
|
||||
output += '\n=== Summary ===\n';
|
||||
output += `Total Sources: ${sources.length}\n`;
|
||||
|
||||
// 确保日志目录存在
|
||||
// Ensure the log directory exists
|
||||
await fs.mkdir(path.join(__dirname, '../logs'), { recursive: true });
|
||||
|
||||
// 写入文件
|
||||
// Write to file
|
||||
await fs.writeFile(filePath, output, 'utf8');
|
||||
|
||||
console.log(`Data exported successfully to: ${filePath}`);
|
||||
@@ -71,7 +71,7 @@ async function exportTrackingData() {
|
||||
console.log(output.slice(0, 500) + '...');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 关闭 Redis 连接
|
||||
// Close Redis connection
|
||||
await redis.quit();
|
||||
|
||||
} catch (error) {
|
||||
@@ -80,5 +80,5 @@ async function exportTrackingData() {
|
||||
}
|
||||
}
|
||||
|
||||
// 执行导出
|
||||
// Execute export
|
||||
exportTrackingData();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import dotenv from "dotenv";
|
||||
import path from "path";
|
||||
|
||||
// 定义配置对象的类型
|
||||
// Define the type for the configuration object
|
||||
interface AppConfig {
|
||||
PORT: number;
|
||||
CORS_ORIGIN: string;
|
||||
@@ -12,23 +12,23 @@ interface AppConfig {
|
||||
};
|
||||
}
|
||||
|
||||
// 根据环境加载对应的.env文件
|
||||
// Load the corresponding .env file based on the environment
|
||||
dotenv.config({
|
||||
path:
|
||||
process.env.NODE_ENV === "production"
|
||||
? path.resolve(process.cwd(), ".env.production.local")
|
||||
: path.resolve(process.cwd(), ".env.development.local"),
|
||||
});
|
||||
// 检查必要的 Redis 环境变量
|
||||
// Check for necessary Redis environment variables
|
||||
if (!process.env.REDIS_HOST) {
|
||||
console.error("FATAL ERROR: REDIS_HOST environment variable is not set.");
|
||||
process.exit(1); // 或者抛出错误 new Error("REDIS_HOST environment variable is not set.");
|
||||
process.exit(1); // Or throw an error: new Error("REDIS_HOST environment variable is not set.");
|
||||
}
|
||||
if (!process.env.REDIS_PORT) {
|
||||
console.error("FATAL ERROR: REDIS_PORT environment variable is not set.");
|
||||
process.exit(1); // 或者抛出错误 new Error("REDIS_PORT environment variable is not set.");
|
||||
process.exit(1); // Or throw an error: new Error("REDIS_PORT environment variable is not set.");
|
||||
}
|
||||
// 导出类型安全的配置对象
|
||||
// Export the type-safe configuration object
|
||||
export const CONFIG: AppConfig = {
|
||||
PORT: parseInt(process.env.BACKEND_PORT || "3001", 10),
|
||||
CORS_ORIGIN: process.env.CORS_ORIGIN!,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CorsOptions } from 'cors';
|
||||
import { CONFIG } from './env';
|
||||
// 配置 CORS
|
||||
// Configure CORS
|
||||
export const corsOptions: CorsOptions = CONFIG.NODE_ENV === 'production'
|
||||
? {
|
||||
origin: CONFIG.CORS_ORIGIN,
|
||||
@@ -9,23 +9,23 @@ export const corsOptions: CorsOptions = CONFIG.NODE_ENV === 'production'
|
||||
allowedHeaders: ['Content-Type', 'Authorization']
|
||||
}
|
||||
: {
|
||||
origin: true, // 开发环境允许所有源
|
||||
origin: true, // Allow all origins in development environment
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization']
|
||||
};
|
||||
// 配置 Socket.IO 的 CORS
|
||||
// Configure CORS for Socket.IO
|
||||
export const corsWSOptions = CONFIG.NODE_ENV === 'production'
|
||||
? {
|
||||
origin: CONFIG.CORS_ORIGIN,// 允许的源,替换为你的Next.js应用的URL
|
||||
origin: CONFIG.CORS_ORIGIN, // Allowed origin, replace with your Next.js application's URL
|
||||
methods: ['GET', 'POST'],
|
||||
credentials: true
|
||||
}
|
||||
: {
|
||||
// 开发环境下允许多个源
|
||||
// Allow multiple origins in development environment
|
||||
origin: [
|
||||
CONFIG.CORS_ORIGIN,
|
||||
/^http:\/\/192\.168\.\d+\.\d+:3000$/,// 匹配所有 192.168.x.x:3000 格式的局域网地址
|
||||
/^http:\/\/192\.168\.\d+\.\d+:3000$/, // Match all LAN addresses in the format 192.168.x.x:3000
|
||||
],
|
||||
methods: ['GET', 'POST'],
|
||||
credentials: true
|
||||
|
||||
+10
-10
@@ -20,7 +20,7 @@ import { ReferrerTrack, LogMessage } from "../types/room";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// 定义接口提高代码可读性和类型安全性
|
||||
// Define interfaces to improve code readability and type safety
|
||||
interface CreateRoomRequest {
|
||||
roomId: string;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ interface CheckRoomRequest {
|
||||
roomId: string;
|
||||
}
|
||||
|
||||
// 创建房间的路由处理函数
|
||||
// Route handler for creating a room
|
||||
const createRoomHandler: RequestHandler<{}, any, CreateRoomRequest> = async (
|
||||
req,
|
||||
res
|
||||
@@ -58,7 +58,7 @@ const createRoomHandler: RequestHandler<{}, any, CreateRoomRequest> = async (
|
||||
}
|
||||
};
|
||||
|
||||
// 获取房间的路由处理函数
|
||||
// Route handler for getting a room
|
||||
const getRoomHandler: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const roomId = await roomService.getAvailableRoomId();
|
||||
@@ -70,7 +70,7 @@ const getRoomHandler: RequestHandler = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 检查房间的路由处理函数
|
||||
// Route handler for checking a room
|
||||
const checkRoomHandler: RequestHandler<{}, any, CheckRoomRequest> = async (
|
||||
req,
|
||||
res
|
||||
@@ -90,7 +90,7 @@ const checkRoomHandler: RequestHandler<{}, any, CheckRoomRequest> = async (
|
||||
}
|
||||
};
|
||||
|
||||
// 设置跟踪的路由处理函数
|
||||
// Route handler for setting tracking
|
||||
const setTrackHandler: RequestHandler<{}, any, ReferrerTrack> = async (
|
||||
req,
|
||||
res
|
||||
@@ -102,16 +102,16 @@ const setTrackHandler: RequestHandler<{}, any, ReferrerTrack> = async (
|
||||
|
||||
try {
|
||||
const { ref, timestamp, path } = req.body;
|
||||
// 按日期统计
|
||||
// Statistics by date
|
||||
const date = new Date(timestamp).toISOString().split("T")[0];
|
||||
const dailyKey = `referrers:daily:${date}`;
|
||||
const thirtyDaysInSeconds = 30 * 24 * 60 * 60;
|
||||
|
||||
// 使用MULTI确保hincrby和expire的原子性
|
||||
// Use MULTI to ensure atomicity of hincrby and expire
|
||||
await redis
|
||||
.multi()
|
||||
.hincrby(dailyKey, ref, 1) // \"referrers:daily:2024-01-20\" : { \"producthunt\": \"5\", \"twitter\": \"3\" }
|
||||
.expire(dailyKey, thirtyDaysInSeconds) // 设置30天过期
|
||||
.expire(dailyKey, thirtyDaysInSeconds) // Set a 30-day expiration
|
||||
.exec();
|
||||
|
||||
res.status(200).json({ success: true });
|
||||
@@ -121,7 +121,7 @@ const setTrackHandler: RequestHandler<{}, any, ReferrerTrack> = async (
|
||||
}
|
||||
};
|
||||
|
||||
// 日志调试的路由处理函数
|
||||
// Route handler for log debugging
|
||||
const logsDebugHandler: RequestHandler<{}, any, LogMessage> = async (
|
||||
req,
|
||||
res
|
||||
@@ -136,7 +136,7 @@ const logsDebugHandler: RequestHandler<{}, any, LogMessage> = async (
|
||||
}
|
||||
};
|
||||
|
||||
// 注册路由
|
||||
// Register routes
|
||||
router.post("/api/create_room", createRoomHandler);
|
||||
router.get("/api/get_room", getRoomHandler);
|
||||
router.post("/api/check_room", checkRoomHandler);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import express from "express"; //express: 用于创建一个简洁且灵活的Node.js web应用框架
|
||||
import express from "express"; // express: A minimalist and flexible Node.js web application framework
|
||||
import cors from "cors";
|
||||
import http from "http";
|
||||
import { Server } from "socket.io"; //实时通信库,基于WebSocket协议,实现双向通信
|
||||
import { Server } from "socket.io"; // socket.io: A library for real-time web applications, enables real-time, bi-directional communication between web clients and servers.
|
||||
import { CONFIG } from "./config/env";
|
||||
import { corsOptions, corsWSOptions } from "./config/server";
|
||||
import apiRouter from "./routes/api";
|
||||
import { setupSocketHandlers } from "./socket/handlers";
|
||||
|
||||
const app = express(); //创建一个Express应用
|
||||
app.use(cors(corsOptions)); // 添加 CORS 中间件
|
||||
const app = express(); // Create an Express application
|
||||
app.use(cors(corsOptions)); // Add CORS middleware
|
||||
app.use(express.json());
|
||||
app.use(apiRouter);
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
import { redis } from './redis';
|
||||
|
||||
const RATE_LIMIT_PREFIX = 'ratelimit:join:';
|
||||
const RATE_WINDOW = 5; // 5秒时间窗口
|
||||
const RATE_LIMIT = 2; // 允许的最大请求次数
|
||||
const RATE_WINDOW = 5; // 5-second time window
|
||||
const RATE_LIMIT = 2; // Maximum number of requests allowed
|
||||
|
||||
export async function checkRateLimit(ip: string): Promise<{
|
||||
allowed: boolean;
|
||||
@@ -32,7 +32,7 @@ export async function checkRateLimit(ip: string): Promise<{
|
||||
const now = Date.now();
|
||||
const windowStart = now - (RATE_WINDOW * 1000);
|
||||
|
||||
// 使用 Redis 的 MULTI 命令开启事务
|
||||
// Use Redis's MULTI command to start a transaction
|
||||
const pipeline = redis.pipeline();
|
||||
|
||||
// 1. Add current request's timestamp
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { Redis } from 'ioredis';
|
||||
import { CONFIG } from '../config/env';
|
||||
// 房间前缀和过期时间(秒)
|
||||
// Room prefix and expiration time (seconds)
|
||||
export const ROOM_PREFIX = 'room:';
|
||||
export const SOCKET_PREFIX = 'socket:';
|
||||
export const ROOM_EXPIRY = 3600 * 24; // 24 hours
|
||||
// Redis 配置选项
|
||||
// Redis configuration options
|
||||
const redisConfig = {
|
||||
host: CONFIG.REDIS.HOST,
|
||||
port: CONFIG.REDIS.PORT,
|
||||
// Redis 持久化配置需要在 redis.conf 中设置,而不是在客户端
|
||||
// appendonly: 'yes',// 启用 AOF 持久化
|
||||
// save: '900 1 300 10',// 启用 RDB 快照
|
||||
// Redis persistence configuration needs to be set in redis.conf, not in the client
|
||||
// appendonly: 'yes',// Enable AOF persistence
|
||||
// save: '900 1 300 10',// Enable RDB snapshot
|
||||
};
|
||||
|
||||
export const redis = new Redis(redisConfig);
|
||||
|
||||
// 可以在这里添加连接事件监听
|
||||
// Connection event listeners can be added here
|
||||
redis.on('connect', () => {
|
||||
console.log('Redis connected successfully');
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ import { redis, ROOM_PREFIX, SOCKET_PREFIX, ROOM_EXPIRY } from './redis';
|
||||
const MAX_NUMERIC_ID_ATTEMPTS = 10;
|
||||
const MAX_ALPHANUMERIC_ID_ATTEMPTS = 50;
|
||||
|
||||
// 生成随机房间号--4位数字
|
||||
// Generate a random 4-digit numeric room ID
|
||||
function generateNumericRoomId(length: number = 4): string {
|
||||
let id = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
@@ -37,6 +37,7 @@ function generateNumericRoomId(length: number = 4): string {
|
||||
}
|
||||
return id;
|
||||
}
|
||||
// Generate a random 4-character alphanumeric room ID
|
||||
function generateAlphanumericRoomId(length: number = 4): string {
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
@@ -46,11 +47,11 @@ function generateAlphanumericRoomId(length: number = 4): string {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// 检查房间是否存在
|
||||
// Check if a room exists
|
||||
export async function isRoomExist(roomId: string): Promise<boolean> {
|
||||
return await redis.hexists(ROOM_PREFIX + roomId, 'created_at') === 1;//hset和hexists方法操作哈希,created_at是字段名
|
||||
return await redis.hexists(ROOM_PREFIX + roomId, 'created_at') === 1; // hset and hexists operate on hashes, 'created_at' is the field name
|
||||
}
|
||||
// 创建新房间
|
||||
// Create a new room
|
||||
// (Hash)
|
||||
// "room:1234" : {
|
||||
// "created_at": "1705123456789"
|
||||
@@ -59,16 +60,16 @@ export async function createRoom(roomId: string): Promise<void> {
|
||||
const roomKey = ROOM_PREFIX + roomId;
|
||||
const socketsKey = `${roomKey}:sockets`;
|
||||
await redis.multi()
|
||||
.hset(roomKey, 'created_at', Date.now())//设置hash,存储房间的创建时间;
|
||||
.expire(roomKey, ROOM_EXPIRY)//设置过期时间
|
||||
.hset(roomKey, 'created_at', Date.now()) // Set hash to store the room's creation time
|
||||
.expire(roomKey, ROOM_EXPIRY) // Set expiration time
|
||||
.expire(socketsKey, ROOM_EXPIRY)
|
||||
.exec();
|
||||
}
|
||||
// 删除房间
|
||||
// Delete a room
|
||||
export async function deleteRoom(roomId: string): Promise<void> {
|
||||
await redis.del(ROOM_PREFIX + roomId);
|
||||
}
|
||||
// 刷新房间过期时间
|
||||
// Refresh a room's expiration time
|
||||
export async function refreshRoom(roomId: string, expiry: number = 0): Promise<void> {
|
||||
const actualExpiry = expiry > 0 ? expiry : ROOM_EXPIRY;
|
||||
const roomKey = ROOM_PREFIX + roomId;
|
||||
@@ -79,7 +80,7 @@ export async function refreshRoom(roomId: string, expiry: number = 0): Promise<v
|
||||
.expire(socketsKey, actualExpiry)
|
||||
.exec();
|
||||
}
|
||||
// 获取可用房间号
|
||||
// Get an available room ID
|
||||
export async function getAvailableRoomId(): Promise<string> {
|
||||
let roomId: string;
|
||||
let attempts = 0;
|
||||
@@ -106,32 +107,32 @@ export async function getAvailableRoomId(): Promise<string> {
|
||||
}
|
||||
return roomId;
|
||||
}
|
||||
// 将socket.id与房间号绑定
|
||||
// Bind a socket.id to a room ID
|
||||
export async function bindSocketToRoom(socketId: string, roomId: string): Promise<void> {
|
||||
await redis.multi()
|
||||
//字符串,存储与该socket ID相关联的房间号,"socket:abcd1234" : "1234"
|
||||
// String, stores the room ID associated with this socket ID, e.g., "socket:abcd1234" : "1234"
|
||||
.set(SOCKET_PREFIX + socketId, roomId, 'EX', ROOM_EXPIRY+3600) // Set with expiry
|
||||
//添加集合,房间内的 Socket 列表 (Set),"room:1234:sockets" : ["socket1", "socket2", ...]
|
||||
// Set, list of sockets in the room, e.g., "room:1234:sockets" : ["socket1", "socket2", ...]
|
||||
.sadd(ROOM_PREFIX + roomId + ':sockets', socketId)
|
||||
.exec();
|
||||
}
|
||||
// 获取socket.id对应的房间号
|
||||
// Get the room ID for a given socket.id
|
||||
export async function getRoomBySocketId(socketId: string): Promise<string | null> {
|
||||
return await redis.get(SOCKET_PREFIX + socketId);
|
||||
}
|
||||
// 解绑socket.id与房间号
|
||||
// Unbind a socket.id from a room ID
|
||||
export async function unbindSocketFromRoom(socketId: string, roomId: string): Promise<void> {
|
||||
await redis.multi()
|
||||
.del(SOCKET_PREFIX + socketId)//解绑socket ID与房间号
|
||||
.srem(ROOM_PREFIX + roomId + ':sockets', socketId)//从房间的集合中移除socket ID
|
||||
.del(SOCKET_PREFIX + socketId) // Unbind socket ID from room ID
|
||||
.srem(ROOM_PREFIX + roomId + ':sockets', socketId) // Remove socket ID from the room's set
|
||||
.exec();
|
||||
}
|
||||
// 检查房间是否为空
|
||||
// Check if a room is empty
|
||||
export async function isRoomEmpty(roomId: string): Promise<boolean> {
|
||||
const count = await redis.scard(ROOM_PREFIX + roomId + ':sockets');//返回集合中元素的数量
|
||||
const count = await redis.scard(ROOM_PREFIX + roomId + ':sockets'); // Returns the number of elements in the set
|
||||
return count === 0;
|
||||
}
|
||||
// 检查房间连接数
|
||||
// Get the number of connections in a room
|
||||
export async function roomNumOfConnection(roomId: string): Promise<number> {
|
||||
return await redis.scard(ROOM_PREFIX + roomId + ':sockets');
|
||||
}
|
||||
@@ -2,12 +2,12 @@ import { Server, Socket } from 'socket.io';
|
||||
import * as roomService from '../services/room';
|
||||
import { JoinData, SignalingData, InitiatorData, RecipientData } from '../types/socket';
|
||||
import { checkRateLimit } from '../services/rateLimit';
|
||||
// 房间管理:
|
||||
// 使用 roomId 进行广播消息(socket.to(roomId).emit())
|
||||
// 场景:新用户加入通知、房间状态更新等
|
||||
// WebRTC 信令:
|
||||
// 使用 peerId 进行点对点通信(socket.to(peerId).emit())
|
||||
// 场景:offer、answer、ice-candidate 等 WebRTC 连接建立过程中的所有信令
|
||||
// Room Management:
|
||||
// Use roomId to broadcast messages (socket.to(roomId).emit())
|
||||
// Scenarios: Notifying new user joined, room status updates, etc.
|
||||
// WebRTC Signaling:
|
||||
// Use peerId for peer-to-peer communication (socket.to(peerId).emit())
|
||||
// Scenarios: All signaling during WebRTC connection setup, like offer, answer, ice-candidate.
|
||||
export function setupSocketHandlers(io: Server): void {
|
||||
io.on('connection', (socket: Socket) => {
|
||||
console.log('New client connected:', socket.id);
|
||||
@@ -15,10 +15,10 @@ export function setupSocketHandlers(io: Server): void {
|
||||
socket.on('join', async (data: JoinData) => {
|
||||
const { roomId: targetRoomId } = data; // Renamed for clarity
|
||||
try {
|
||||
// 获取客户端IP
|
||||
// Get client IP
|
||||
const clientIp = socket.handshake.headers['x-forwarded-for'] ||
|
||||
socket.handshake.address;
|
||||
// 检查频率限制
|
||||
// Check rate limit
|
||||
const rateLimitCheck = await checkRateLimit(clientIp as string);
|
||||
if (!rateLimitCheck.allowed) {
|
||||
socket.emit('joinResponse', {
|
||||
@@ -35,16 +35,16 @@ export function setupSocketHandlers(io: Server): void {
|
||||
}
|
||||
|
||||
const existingRoomId = await roomService.getRoomBySocketId(socket.id);
|
||||
if (!existingRoomId) {//socket.id不在房间里面 才允许新连接进入房间
|
||||
if (!existingRoomId) { // Only allow new connection to join if the socket.id is not already in a room
|
||||
socket.join(targetRoomId);
|
||||
console.log(`Client ${socket.id} joined room ${targetRoomId}`);
|
||||
await roomService.bindSocketToRoom(socket.id, targetRoomId);
|
||||
}
|
||||
|
||||
await roomService.refreshRoom(targetRoomId);
|
||||
// 通知用户加入成功
|
||||
// Notify the user that the join was successful
|
||||
socket.emit('joinResponse', { success: true, message: 'Successfully joined room', roomId: targetRoomId });
|
||||
// 通知房间内所有其他用户有新成员加入
|
||||
// Notify all other users in the room that a new member has joined
|
||||
socket.to(targetRoomId).emit('ready', { peerId: socket.id });
|
||||
} catch (error) {
|
||||
console.error('Error joining room:', error);
|
||||
@@ -55,16 +55,16 @@ export function setupSocketHandlers(io: Server): void {
|
||||
});
|
||||
}
|
||||
});
|
||||
// 处理WebRTC信令--直接转发
|
||||
// offer, answer, ice-candidate: 这些事件处理WebRTC连接的信令。它们负责转发客户端之间的连接请求和网络协商消息。
|
||||
// offer: 当一个客户端发起连接请求时,会发送一个offer给服务器,服务器将其转发给相同房间中的其他客户端。
|
||||
// answer: 被邀请的客户端接收到offer后,生成一个answer,通过服务器返回给发起连接的客户端。
|
||||
// ice-candidate: 当WebRTC需要穿透NAT防火墙时,会生成ICE候选者,客户端通过服务器相互交换这些信息,帮助建立P2P连接。
|
||||
// Handle WebRTC signaling - direct forwarding
|
||||
// offer, answer, ice-candidate: These events handle WebRTC connection signaling. They are responsible for forwarding connection requests and network negotiation messages between clients.
|
||||
// offer: When a client initiates a connection request, it sends an offer to the server, which forwards it to other clients in the same room.
|
||||
// answer: The invited client receives the offer, generates an answer, and sends it back to the initiating client via the server.
|
||||
// ice-candidate: When WebRTC needs to traverse NAT firewalls, it generates ICE candidates. Clients exchange this information through the server to help establish a P2P connection.
|
||||
socket.on('offer', (data: SignalingData) => {
|
||||
socket.to(data.peerId).emit('offer', {
|
||||
offer: data.offer,
|
||||
from: data.from,
|
||||
peerId: socket.id // 发送方的ID
|
||||
peerId: socket.id // Sender's ID
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,13 +83,13 @@ export function setupSocketHandlers(io: Server): void {
|
||||
peerId: socket.id
|
||||
});
|
||||
});
|
||||
// 处理发起方重新上线的通知--广播给房间内的其他用户
|
||||
// Handle notification for initiator coming back online -- broadcast to other users in the room
|
||||
socket.on('initiator-online', (data: InitiatorData) => {
|
||||
socket.to(data.roomId).emit('initiator-online', {
|
||||
roomId: data.roomId
|
||||
});
|
||||
});
|
||||
// 处理接收方的响应
|
||||
// Handle recipient's response
|
||||
socket.on('recipient-ready', (data: RecipientData) => {
|
||||
socket.to(data.roomId).emit('recipient-ready', {
|
||||
peerId: data.peerId
|
||||
|
||||
@@ -2,7 +2,7 @@ export interface JoinData {
|
||||
roomId: string;
|
||||
}
|
||||
|
||||
// 添加 WebRTC 相关类型定义
|
||||
// Add WebRTC related type definitions
|
||||
declare global {
|
||||
interface RTCSessionDescriptionInit {
|
||||
type: RTCSdpType;
|
||||
|
||||
Reference in New Issue
Block a user