Files
video_socket-server/src/class/httphandler.ts

1129 lines
35 KiB
TypeScript
Raw Normal View History

2026-04-29 15:18:30 +08:00
/**
* HTTP处理器
* HTTP请求
*/
import { Request, Response } from 'express';
import Offer from './offer';
import Answer from './answer';
import Candidate from './candidate';
import { v4 as uuid } from 'uuid';
import { onGetAllConnectionIds } from './websockethandler';
/**
*
*
*/
class Disconnection {
id: string; // 连接ID
datetime: number; // 断开连接的时间戳
/**
*
* @param id ID
* @param datetime
*/
constructor(id: string, datetime: number) {
this.id = id;
this.datetime = datetime;
}
}
// 会话超时时间(毫秒)
const TimeoutRequestedTime = 10000; // 10sec
// 是否为私有模式
let isPrivate: boolean;
/**
*
* : 会话ID
* : 该会话的连接ID集合
*/
const clients: Map<string, Set<string>> = new Map<string, Set<string>>();
/**
*
* : 会话ID
* : 最后请求的时间戳
*/
const lastRequestedTime: Map<string, number> = new Map<string, number>();
/**
*
* : 连接ID
* : [ID1, ID2]
*/
const connectionPair: Map<string, [string, string]> = new Map<string, [string, string]>(); // key = connectionId
/**
* offer映射
* : 会话ID
* : 该会话的连接ID到Offer对象的映射
*/
const offers: Map<string, Map<string, Offer>> = new Map<string, Map<string, Offer>>(); // key = sessionId
/**
* answer映射
* : 会话ID
* : 该会话的连接ID到Answer对象的映射
*/
const answers: Map<string, Map<string, Answer>> = new Map<string, Map<string, Answer>>(); // key = sessionId
/**
* candidate映射
* : 会话ID
* : 该会话的连接ID到Candidate数组的映射
*/
const candidates: Map<string, Map<string, Candidate[]>> = new Map<string, Map<string, Candidate[]>>(); // key = sessionId
/**
*
* : 会话ID
* : 该会话的Disconnection对象数组
*/
const disconnections: Map<string, Disconnection[]> = new Map<string, Disconnection[]>(); // key = sessionId
/**
* ID集合
* @param sessionId ID
* @returns ID的Set集合
*/
function getOrCreateConnectionIds(sessionId: string): Set<string> {
let connectionIds = null;
// 检查会话是否已存在
if (!clients.has(sessionId)) {
// 如果不存在创建新的连接ID集合
connectionIds = new Set<string>();
// 将新的连接ID集合与会话关联
clients.set(sessionId, connectionIds);
}
// 获取会话的连接ID集合
connectionIds = clients.get(sessionId);
// 返回连接ID集合
return connectionIds;
}
/**
*
* @param mode public或private
*/
function reset(mode: string): void {
// 设置是否为私有模式
isPrivate = mode == "private";
// 清空所有映射
clients.clear();
connectionPair.clear();
offers.clear();
answers.clear();
candidates.clear();
disconnections.clear();
}
/**
* ID是否有效
* @param req Express请求对象
* @param res Express响应对象
* @param next
*/
function checkSessionId(req: Request, res: Response, next): void {
// 如果是根路径,直接通过
if (req.url === '/') {
next();
return;
}
// 从请求头获取会话ID
const id: string = req.header('session-id');
// 检查会话是否存在
if (!clients.has(id)) {
res.sendStatus(404);
return;
}
// 更新会话的最后请求时间
lastRequestedTime.set(id, Date.now());
// 继续处理请求
next();
}
/**
*
* @param sessionId ID
* @param connectionId ID
* @param datetime
*/
function _deleteConnection(sessionId:string, connectionId:string, datetime:number) {
// 从会话的连接ID集合中删除连接ID
clients.get(sessionId).delete(connectionId);
// 处理私有模式
if(isPrivate) {
if (connectionPair.has(connectionId)) {
const pair = connectionPair.get(connectionId);
// 找到另一个会话ID
const otherSessionId = pair[0] == sessionId ? pair[1] : pair[0];
if (otherSessionId) {
if (clients.has(otherSessionId)) {
// 从另一个会话的连接ID集合中删除连接ID
clients.get(otherSessionId).delete(connectionId);
// 向另一个会话的断开连接记录中添加记录
const array1 = disconnections.get(otherSessionId);
array1.push(new Disconnection(connectionId, datetime));
}
}
}
} else {
// 公共模式:向所有其他会话的断开连接记录中添加记录
disconnections.forEach((array, id) => {
if (id == sessionId)
return;
array.push(new Disconnection(connectionId, datetime));
});
}
// 从连接对映射中删除
connectionPair.delete(connectionId);
// 从会话的offer映射中删除
offers.get(sessionId).delete(connectionId);
// 从会话的answer映射中删除
answers.get(sessionId).delete(connectionId);
// 从会话的candidate映射中删除
candidates.get(sessionId).delete(connectionId);
// 向当前会话的断开连接记录中添加记录
const array2 = disconnections.get(sessionId);
array2.push(new Disconnection(connectionId, datetime));
}
/**
*
* @param sessionId ID
*/
function _deleteSession(sessionId: string) {
// 如果会话存在,删除其所有连接
if(clients.has(sessionId)) {
for(const connectionId of Array.from(clients.get(sessionId))) {
_deleteConnection(sessionId, connectionId, Date.now());
}
}
// 从所有映射中删除会话
offers.delete(sessionId);
answers.delete(sessionId);
candidates.delete(sessionId);
clients.delete(sessionId);
disconnections.delete(sessionId);
}
/**
*
*/
function _checkForTimedOutSessions(): void {
// 遍历所有会话
for (const sessionId of Array.from(clients.keys()))
{
// 如果会话没有最后请求时间,跳过
if(!lastRequestedTime.has(sessionId))
continue;
// 如果会话未超时,跳过
if(lastRequestedTime.get(sessionId) > Date.now() - TimeoutRequestedTime)
continue;
// 删除超时会话
_deleteSession(sessionId);
console.log(`deleted sessionId:${sessionId} by timeout.`);
}
}
/**
* ID列表
* @param sessionId ID
* @returns ID数组
*/
function _getConnection(sessionId: string): string[] {
// 检查超时会话
_checkForTimedOutSessions();
// 返回会话的连接ID集合的数组形式
return Array.from(clients.get(sessionId));
}
/**
*
* @param sessionId ID
* @param fromTime
* @returns
*/
function _getDisconnection(sessionId: string, fromTime: number): Disconnection[] {
// 检查超时会话
_checkForTimedOutSessions();
let arrayDisconnections: Disconnection[] = [];
// 如果断开连接记录存在,获取该会话的断开连接记录
if (disconnections.size != 0 && disconnections.has(sessionId)) {
arrayDisconnections = disconnections.get(sessionId);
}
// 如果指定了起始时间,过滤出时间戳大于等于起始时间的记录
if (fromTime > 0) {
arrayDisconnections = arrayDisconnections.filter((v) => v.datetime >= fromTime);
}
return arrayDisconnections;
}
/**
* offer列表
* @param sessionId ID
* @param fromTime
* @returns [ID, Offer]
*/
function _getOffer(sessionId: string, fromTime: number): [string, Offer][] {
let arrayOffers: [string, Offer][] = [];
// 如果offer映射不为空
if (offers.size != 0) {
// 处理私有模式
if (isPrivate) {
// 如果会话存在offer记录获取该会话的offer列表
if (offers.has(sessionId)) {
arrayOffers = Array.from(offers.get(sessionId));
}
} else {
// 公共模式获取所有其他会话的offer列表
const otherSessionMap = Array.from(offers).filter(x => x[0] != sessionId);
arrayOffers = [].concat(...Array.from(otherSessionMap, x => Array.from(x[1], y => [y[0], y[1]])));
}
}
// 如果指定了起始时间过滤出时间戳大于等于起始时间的offer
if (fromTime > 0) {
arrayOffers = arrayOffers.filter((v) => v[1].datetime >= fromTime);
}
return arrayOffers;
}
/**
* answer列表
* @param sessionId ID
* @param fromTime
* @returns [ID, Answer]
*/
function _getAnswer(sessionId: string, fromTime: number): [string, Answer][] {
let arrayAnswers: [string, Answer][] = [];
// 如果answer映射不为空且会话存在answer记录获取该会话的answer列表
if (answers.size != 0 && answers.has(sessionId)) {
arrayAnswers = Array.from(answers.get(sessionId));
}
// 如果指定了起始时间过滤出时间戳大于等于起始时间的answer
if (fromTime > 0) {
arrayAnswers = arrayAnswers.filter((v) => v[1].datetime >= fromTime);
}
return arrayAnswers;
}
/**
* candidate列表
* @param sessionId ID
* @param fromTime
* @returns [ID, Candidate]
*/
function _getCandidate(sessionId: string, fromTime: number): [string, Candidate][] {
// 获取会话的连接ID列表
const connectionIds = Array.from(clients.get(sessionId));
const arr: [string, Candidate][] = [];
// 遍历每个连接ID
for (const connectionId of connectionIds) {
// 获取连接对
const pair = connectionPair.get(connectionId);
if (pair == null) {
continue;
}
// 找到另一个会话ID
const otherSessionId = sessionId === pair[0] ? pair[1] : pair[0];
// 如果另一个会话不存在candidate记录或该连接ID不存在candidate记录跳过
if (!candidates.get(otherSessionId) || !candidates.get(otherSessionId).get(connectionId)) {
continue;
}
// 获取该连接ID的candidate列表并过滤出时间戳大于等于起始时间的candidate
const arrayCandidates = candidates.get(otherSessionId).get(connectionId)
.filter((v) => v.datetime >= fromTime);
// 如果没有符合条件的candidate跳过
if (arrayCandidates.length === 0) {
continue;
}
// 将符合条件的candidate添加到结果数组中
for (const candidate of arrayCandidates) {
arr.push([connectionId, candidate]);
}
}
return arr;
}
/**
* @swagger
* /signaling/answer:
* get:
* summary: 获取answer列表
* description: 获取当前会话的answer信令消息列表
* security:
* - sessionAuth: []
* parameters:
* - in: query
* name: fromtime
* schema:
* type: number
* description: 起始时间戳
* responses:
* 200:
* description: 成功获取answer列表
* content:
* application/json:
* schema:
* type: object
* properties:
* answers:
* type: array
* items:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* sdp:
* type: string
* description: SDP描述
* type:
* type: string
* description: 消息类型
* datetime:
* type: number
* description: 时间戳
*/
function getAnswer(req: Request, res: Response): void {
// 从请求查询参数中获取`fromtime`参数
const fromTime: number = req.query.fromtime ? Number(req.query.fromtime) : 0;
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 获取会话的answer列表
const answers: [string, Answer][] = _getAnswer(sessionId, fromTime);
// 返回JSON响应包含answer列表
res.json({ answers: answers.map((v) => ({ connectionId: v[0], sdp: v[1].sdp, type: "answer", datetime: v[1].datetime })) });
}
/**
* @swagger
* /signaling/connection:
* get:
* summary: 获取连接列表
* description: 获取当前会话的连接列表
* security:
* - sessionAuth: []
* responses:
* 200:
* description: 成功获取连接列表
* content:
* application/json:
* schema:
* type: object
* properties:
* connections:
* type: array
* items:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* type:
* type: string
* description: 消息类型
* datetime:
* type: number
* description: 时间戳
*/
function getConnection(req: Request, res: Response): void {
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 获取会话的连接ID列表
const connections = _getConnection(sessionId);
// 返回JSON响应包含连接列表
res.json({ connections: connections.map((v) => ({ connectionId: v, type: "connect", datetime: Date.now() })) });
}
/**
* @swagger
* /signaling/offer:
* get:
* summary: 获取offer列表
* description: 获取当前会话的offer信令消息列表
* security:
* - sessionAuth: []
* parameters:
* - in: query
* name: fromtime
* schema:
* type: number
* description: 起始时间戳
* responses:
* 200:
* description: 成功获取offer列表
* content:
* application/json:
* schema:
* type: object
* properties:
* offers:
* type: array
* items:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* sdp:
* type: string
* description: SDP描述
* polite:
* type: boolean
* description: 是否为polite模式
* type:
* type: string
* description: 消息类型
* datetime:
* type: number
* description: 时间戳
*/
function getOffer(req: Request, res: Response): void {
// 从请求查询参数中获取`fromtime`参数
const fromTime: number = req.query.fromtime ? Number(req.query.fromtime) : 0;
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 获取会话的offer列表
const offers = _getOffer(sessionId, fromTime);
// 返回JSON响应包含offer列表
res.json({ offers: offers.map((v) => ({ connectionId: v[0], sdp: v[1].sdp, polite: v[1].polite, type: "offer", datetime: v[1].datetime })) });
}
/**
* @swagger
* /signaling/candidate:
* get:
* summary: 获取candidate列表
* description: 获取当前会话的candidate信令消息列表
* security:
* - sessionAuth: []
* parameters:
* - in: query
* name: fromtime
* schema:
* type: number
* description: 起始时间戳
* responses:
* 200:
* description: 成功获取candidate列表
* content:
* application/json:
* schema:
* type: object
* properties:
* candidates:
* type: array
* items:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* candidate:
* type: string
* description: ICE候选者信息
* sdpMLineIndex:
* type: number
* description: SDP媒体行索引
* sdpMid:
* type: string
* description: SDP媒体ID
* type:
* type: string
* description: 消息类型
* datetime:
* type: number
* description: 时间戳
*/
function getCandidate(req: Request, res: Response): void {
// 从请求查询参数中获取`fromtime`参数
const fromTime: number = req.query.fromtime ? Number(req.query.fromtime) : 0;
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 获取会话的candidate列表
const candidates = _getCandidate(sessionId, fromTime);
// 返回JSON响应包含candidate列表
res.json({ candidates: candidates.map((v) => ({ connectionId: v[0], candidate: v[1].candidate, sdpMLineIndex: v[1].sdpMLineIndex, sdpMid: v[1].sdpMid, type: "candidate", datetime: v[1].datetime })) });
}
/**
* @swagger
* /signaling:
* get:
* summary: 获取所有信令消息
* description: 获取当前会话的所有信令消息offeranswer和candidate
* security:
* - sessionAuth: []
* parameters:
* - in: query
* name: fromtime
* schema:
* type: number
* description: 起始时间戳
* responses:
* 200:
* description: 成功获取所有信令消息
* content:
* application/json:
* schema:
* type: object
* properties:
* messages:
* type: array
* items:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* type:
* type: string
* description: 消息类型
* datetime:
* type: number
* description: 时间戳
* sdp:
* type: string
* description: SDP描述offer和answer消息
* polite:
* type: boolean
* description: 是否为polite模式offer消息
* candidate:
* type: string
* description: ICE候选者信息candidate消息
* sdpMLineIndex:
* type: number
* description: SDP媒体行索引candidate消息
* sdpMid:
* type: string
* description: SDP媒体IDcandidate消息
* datetime:
* type: number
* description: 当前时间戳
*/
function getAll(req: Request, res: Response): void {
// 从请求查询参数中获取`fromtime`参数
const fromTime: number = req.query.fromtime ? Number(req.query.fromtime) : 0;
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 获取各种信令消息
const connections = _getConnection(sessionId);
const offers = _getOffer(sessionId, fromTime);
const answers: [string, Answer][] = _getAnswer(sessionId, fromTime);
const candidates: [string, Candidate][] = _getCandidate(sessionId, fromTime);
const disconnections: Disconnection[] = _getDisconnection(sessionId, fromTime);
const datetime = lastRequestedTime.get(sessionId);
let array: any[] = [];
// 合并所有信令消息
array = array.concat(connections.map((v) => ({ connectionId: v, type: "connect", datetime: datetime })));
array = array.concat(offers.map((v) => ({ connectionId: v[0], sdp: v[1].sdp, polite: v[1].polite, type: "offer", datetime: v[1].datetime })));
array = array.concat(answers.map((v) => ({ connectionId: v[0], sdp: v[1].sdp, type: "answer", datetime: v[1].datetime })));
array = array.concat(candidates.map((v) => ({ connectionId: v[0], candidate: v[1].candidate, sdpMLineIndex: v[1].sdpMLineIndex, sdpMid: v[1].sdpMid, type: "candidate", datetime: v[1].datetime })));
array = array.concat(disconnections.map((v) => ({ connectionId: v.id, type: "disconnect", datetime: v.datetime })));
// 按时间戳排序
array.sort((a, b) => a.datetime - b.datetime);
// 返回JSON响应包含所有信令消息
res.json({ messages: array, datetime: datetime });
}
/**
* @swagger
* /signaling:
* put:
* summary: 创建会话
* description: 创建一个新的会话ID
* responses:
* 200:
* description: 成功创建会话
* content:
* application/json:
* schema:
* type: object
* properties:
* sessionId:
* type: string
* description: 新创建的会话ID
*/
function createSession(sessionId: string, res: Response): void;
function createSession(req: Request, res: Response): void;
function createSession(req: string | Request, res: Response): void {
// 确定会话ID
const sessionId: string = typeof req === "string" ? req : uuid();
// 为会话创建各种映射
clients.set(sessionId, new Set<string>());
offers.set(sessionId, new Map<string, Offer>());
answers.set(sessionId, new Map<string, Answer>());
candidates.set(sessionId, new Map<string, Candidate[]>());
disconnections.set(sessionId, []);
// 返回JSON响应包含会话ID
res.json({ sessionId: sessionId });
}
/**
* @swagger
* /signaling:
* delete:
* summary: 删除会话
* description: 删除当前会话及其所有连接
* security:
* - sessionAuth: []
* responses:
* 200:
* description: 成功删除会话
*/
function deleteSession(req: Request, res: Response): void {
// 从请求头获取会话ID
const id: string = req.header('session-id');
// 删除会话
_deleteSession(id);
// 返回200状态码表示请求处理成功
res.sendStatus(200);
}
/**
* @swagger
* /signaling/connection:
* put:
* summary: 创建连接
* description: 创建一个新的连接
* security:
* - sessionAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* responses:
* 200:
* description: 成功创建连接
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* polite:
* type: boolean
* description: 是否为polite模式
* type:
* type: string
* description: 消息类型
* datetime:
* type: number
* description: 时间戳
* 400:
* description: 请求参数错误
*/
function createConnection(req: Request, res: Response): void {
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 从请求体获取连接ID
const { connectionId } = req.body;
// 获取会话的最后请求时间
const datetime = lastRequestedTime.get(sessionId);
// 检查连接ID是否存在
if (connectionId == null) {
res.status(400).send({ error: new Error(`connectionId is required`) });
return;
}
let polite = true;
// 处理私有模式
if (isPrivate) {
if (connectionPair.has(connectionId)) {
const pair = connectionPair.get(connectionId);
// 检查连接ID是否已被使用
if (pair[0] != null && pair[1] != null) {
const err = new Error(`${connectionId}: This connection id is already used.`);
console.log(err);
res.status(400).send({ error: err });
return;
} else if (pair[0] != null) {
// 找到配对连接
connectionPair.set(connectionId, [pair[0], sessionId]);
// 添加连接ID到另一个会话
const map = getOrCreateConnectionIds(pair[0]);
map.add(connectionId);
}
} else {
// 创建新的连接对
connectionPair.set(connectionId, [sessionId, null]);
polite = false;
}
}
// 添加连接ID到当前会话
const connectionIds = getOrCreateConnectionIds(sessionId);
connectionIds.add(connectionId);
// 返回JSON响应包含连接信息
res.json({ connectionId: connectionId, polite: polite, type: "connect", datetime: datetime });
}
/**
* @swagger
* /signaling/connection:
* delete:
* summary: 删除连接
* description: 删除指定的连接
* security:
* - sessionAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* responses:
* 200:
* description: 成功删除连接
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
*/
function deleteConnection(req: Request, res: Response): void {
// 从请求头获取会话ID
const sessionId: string = req.header('session-id');
// 从请求体获取连接ID
const { connectionId } = req.body;
// 获取会话的最后请求时间
const datetime = lastRequestedTime.get(sessionId);
// 删除连接
_deleteConnection(sessionId, connectionId, datetime);
// 返回JSON响应包含连接ID
res.json({ connectionId: connectionId });
}
/**
* @swagger
* /signaling/offer:
* post:
* summary: 发送offer信令
* description: 发送offer信令消息
* security:
* - sessionAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* sdp:
* type: string
* description: SDP描述
* responses:
* 200:
* description: 成功发送offer信令
*/
function postOffer(req: Request, res: Response): void {
const sessionId: string = req.header('session-id');
const { connectionId } = req.body;
const datetime = lastRequestedTime.get(sessionId);
let keySessionId = null;
let polite = false;
if (isPrivate) {
if (connectionPair.has(connectionId)) {
const pair = connectionPair.get(connectionId);
keySessionId = pair[0] == sessionId ? pair[1] : pair[0];
if (keySessionId != null) {
polite = true;
const map = offers.get(keySessionId);
map.set(connectionId, new Offer(req.body.sdp, datetime, polite));
}
}
res.sendStatus(200);
return;
}
if(!connectionPair.has(connectionId))
{
connectionPair.set(connectionId, [sessionId, null]);
}
keySessionId = sessionId;
const map = offers.get(keySessionId);
map.set(connectionId, new Offer(req.body.sdp, datetime, polite));
res.sendStatus(200);
}
/**
* @swagger
* /signaling/answer:
* post:
* summary: 发送answer信令
* description: 发送answer信令消息
* security:
* - sessionAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* sdp:
* type: string
* description: SDP描述
* responses:
* 200:
* description: 成功发送answer信令
*/
function postAnswer(req: Request, res: Response): void {
const sessionId: string = req.header('session-id');
const { connectionId } = req.body;
const datetime = lastRequestedTime.get(sessionId);
const connectionIds = getOrCreateConnectionIds(sessionId);
connectionIds.add(connectionId);
if (!connectionPair.has(connectionId)) {
res.sendStatus(200);
return;
}
// add connectionPair
const pair = connectionPair.get(connectionId);
const otherSessionId = pair[0] == sessionId ? pair[1] : pair[0];
if (!clients.has(otherSessionId)) {
// already deleted
res.sendStatus(200);
return;
}
if (!isPrivate) {
connectionPair.set(connectionId, [otherSessionId, sessionId]);
}
const map = answers.get(otherSessionId);
map.set(connectionId, new Answer(req.body.sdp, datetime));
// update datetime for candidates
const mapCandidates = candidates.get(otherSessionId);
if (mapCandidates) {
const arrayCandidates = mapCandidates.get(connectionId);
if (arrayCandidates) {
for (const candidate of arrayCandidates) {
candidate.datetime = datetime;
}
}
}
res.sendStatus(200);
}
/**
* @swagger
* /signaling/candidate:
* post:
* summary: 发送candidate信令
* description: 发送candidate信令消息
* security:
* - sessionAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionId:
* type: string
* description: 连接ID
* candidate:
* type: string
* description: ICE候选者信息
* sdpMLineIndex:
* type: number
* description: SDP媒体行索引
* sdpMid:
* type: string
* description: SDP媒体ID
* responses:
* 200:
* description: 成功发送candidate信令
*/
function postCandidate(req: Request, res: Response): void {
const sessionId: string = req.header('session-id');
const { connectionId } = req.body;
const datetime = lastRequestedTime.get(sessionId);
const map = candidates.get(sessionId);
if (!map.has(connectionId)) {
map.set(connectionId, []);
}
const arr = map.get(connectionId);
const candidate = new Candidate(req.body.candidate, req.body.sdpMLineIndex, req.body.sdpMid, datetime);
arr.push(candidate);
res.sendStatus(200);
}
/**
* @swagger
* /signaling/rooms:
* get:
* summary: 获取房间和用户信息
* description: 获取所有房间的信息ID和链接的用户
* security:
* - sessionAuth: []
* responses:
* 200:
* description: 成功获取房间和用户信息
* content:
* application/json:
* schema:
* type: object
* properties:
* rooms:
* type: array
* items:
* type: object
* properties:
* roomId:
* type: string
* description: 房间ID
* users:
* type: array
* items:
* type: object
* properties:
* sessionId:
* type: string
* description: 会话ID
* connected:
* type: boolean
* description: 连接状态
* userCount:
* type: number
* description: 用户数量
* totalRooms:
* type: number
* description: 总房间数
*/
function onGetConnections(req: Request, res: Response): void {
// 收集所有房间ID和链接用户信息
const rooms = [];
// 遍历所有连接对
for (const [connectionId, pair] of Array.from(connectionPair.entries())) {
// 收集房间中的用户信息
const users = [];
// 添加第一个用户
if (pair[0] && clients.has(pair[0])) {
users.push({
sessionId: pair[0],
connected: true
});
}
// 添加第二个用户
if (pair[1] && clients.has(pair[1])) {
users.push({
sessionId: pair[1],
connected: true
});
}
// 添加房间信息
rooms.push({
roomId: connectionId,
users: users,
userCount: users.length
});
}
res.json({ rooms: rooms, totalRooms: rooms.length });
}
/**
* @swagger
* /signaling/connection-ids:
* get:
* summary: 获取所有连接ID
* description: 获取所有当前活跃的连接ID
* security:
* - sessionAuth: []
* responses:
* 200:
* description: 成功获取连接ID列表
* content:
* application/json:
* schema:
* type: object
* properties:
* connectionIds:
* type: array
* items:
* type: string
* description: 连接ID
* totalCount:
* type: number
* description: 总连接数
*/
function getAllConnectionIds(req: Request, res: Response): void {
// 获取所有连接ID
const connectionIds = onGetAllConnectionIds();
// 返回JSON响应包含连接ID列表和总数量
res.json({ connectionIds: connectionIds, totalCount: connectionIds.length });
}
/**
* HTTP处理器函数
*/
export {
reset, // 重置处理器状态
checkSessionId, // 检查会话ID是否有效
getAll, // 获取所有信令消息
getConnection, // 获取连接列表
getOffer, // 获取offer列表
getAnswer, // 获取answer列表
getCandidate, // 获取candidate列表
createSession, // 创建会话
deleteSession, // 删除会话
createConnection, // 创建连接
deleteConnection, // 删除连接
postOffer, // 处理offer信令消息
postAnswer, // 处理answer信令消息
postCandidate, // 处理candidate信令消息
onGetConnections, // 获取房间和用户信息
getAllConnectionIds // 获取所有连接ID
};