14、房间管理:房间创建与加入、用户列表管理、房间内信令交互
房间管理,说白了就是整个视频会议APP的"门禁系统"。你想想看,用户打开APP,第一件事是什么?肯定是进房间。房间怎么创建?怎么加入?进去之后怎么知道谁在?这些看似基础的功能,其实藏着不少坑。
我个人习惯把房间管理拆成三个核心模块:房间生命周期管理、用户列表同步、信令交互路由。这三个模块配合好了,整个会议系统才能稳定跑起来。
14.1 房间创建与加入流程
先说说房间创建。我在项目中遇到过一种情况:用户疯狂点击"创建房间"按钮,结果后端收到了N个创建请求,生成了N个房间。嗯,这显然不对。
正确的做法是:前端做防抖,后端做幂等。前端防抖防止用户误操作,后端幂等保证同一个用户在同一时间只能创建一个房间。
房间创建的核心流程是这样的:
// 房间创建请求体
{
"roomId": "uuid-xxxx", // 前端生成,保证唯一
"userId": "user-123",
"userName": "张三",
"roomConfig": {
"maxUsers": 8,
"enableVideo": true,
"enableAudio": true
}
}
// 后端返回
{
"code": 200,
"data": {
"roomId": "uuid-xxxx",
"createdAt": 1695000000,
"token": "room-token-xxx" // 用于后续信令鉴权
}
}
加入房间的逻辑稍微复杂一点。我记得有一次调试,用户加入房间后一直收不到其他人的视频流,查了半天发现是加入房间的信令没带token,服务端直接拒绝了媒体协商请求。
加入房间的完整流程我建议这样设计:
- 用户输入房间ID(或通过邀请链接进入)
- 前端校验房间ID格式(比如UUID格式、长度限制)
- 发送加入请求到后端,携带用户信息和房间ID
- 后端验证房间是否存在、是否已满、用户是否有权限
- 验证通过后,返回房间信息和token
- 前端拿到token,初始化WebRTC PeerConnection
- 连接信令服务器,开始媒体协商
14.2 用户列表管理
用户列表,说白了就是"房间里都有谁"。这个功能看起来简单,但要做到实时、准确、不丢人,其实挺考验设计的。
我见过很多方案是用轮询:每隔几秒拉一次用户列表。但这样做有两个问题:一是延迟高,二是浪费带宽。你想想看,如果房间里10个人,每个人都在轮询,那信令服务器的压力就大了。
更好的方案是基于信令通道的增量同步。什么意思呢?就是用户加入、离开、更新信息时,服务端主动推送变更事件,客户端只更新变化的部分。
| 事件类型 | 触发时机 | 推送内容 |
|---|---|---|
| user-joined | 用户加入房间 | 用户ID、用户名、媒体状态 |
| user-left | 用户离开房间 | 用户ID |
| user-updated | 用户切换摄像头/麦克风 | 用户ID、新的媒体状态 |
| user-muted | 用户静音 | 用户ID、静音类型(audio/video) |
我曾经踩过一个坑:用户断网重连后,用户列表全空了。原因是服务端在用户断开WebSocket时直接删除了用户记录,但客户端重连后没有重新拉取全量列表。
用户列表的数据结构,我个人习惯这样设计:
// 用户对象
{
"userId": "user-123",
"userName": "张三",
"avatar": "https://...", // 头像URL
"mediaState": {
"audioEnabled": true,
"videoEnabled": false,
"screenSharing": false
},
"joinedAt": 1695000000,
"isHost": true // 是否是房主
}
// 用户列表(Map结构,key为userId)
{
"user-123": { ... },
"user-456": { ... },
"user-789": { ... }
}
用Map而不是数组,好处是查找和更新都是O(1)复杂度。你想想看,如果用户列表有100人,用数组查找某个用户要遍历100次,用Map直接取就行了。
14.3 房间内信令交互
信令交互,是整个房间管理的"神经系统"。所有的房间操作、媒体协商、状态同步,都通过信令通道来传递。
我习惯把信令消息分为三类:
- 房间信令:创建房间、加入房间、离开房间、解散房间
- 媒体信令:offer、answer、ICE candidate、媒体状态变更
- 数据信令:聊天消息、文件传输、白板数据等
信令消息的格式,我建议统一使用JSON,包含以下几个核心字段:
{
"type": "signal", // 消息类型
"action": "offer", // 具体动作
"from": "user-123", // 发送者
"to": "user-456", // 接收者(广播时为空)
"roomId": "room-xxx", // 房间ID
"payload": { ... }, // 具体数据
"timestamp": 1695000000, // 时间戳
"token": "xxx" // 鉴权token
}
信令交互的流程,我用一张图来展示:
这张图展示了一个完整的信令交互流程。注意看,所有的信令都经过服务器中转,服务器负责鉴权、路由和广播。但媒体数据(音视频流)是P2P直连的,不经过服务器。
为什么会这样设计?因为信令数据量小,但要求可靠;媒体数据量大,但可以容忍少量丢包。让服务器处理信令,P2P传输媒体,各司其职。
14.4 房间状态管理
房间状态管理,说白了就是"房间里现在是什么情况"。我建议客户端维护一个房间状态对象,包含以下信息:
// 房间状态
{
"roomId": "room-xxx",
"roomName": "张三的会议室",
"hostId": "user-123",
"users": { ... }, // 用户列表
"mediaState": {
"isRecording": false, // 是否在录制
"isScreenSharing": false // 是否在屏幕共享
},
"createdAt": 1695000000,
"maxUsers": 8
}
这个状态对象要保持与服务器同步。每次收到信令事件,都要更新本地状态。我习惯用状态管理库(比如Redux、MobX)来管理,这样UI层可以自动响应状态变化。
嗯,这里要注意:不要直接修改状态对象,要创建新的副本。否则React等框架无法检测到变化,UI不会更新。
最后说一句,房间管理是整个视频会议APP的基石。如果房间管理做不好,后面的媒体协商、音视频传输都是空中楼阁。我建议你在开发初期就把房间管理的逻辑理清楚,多写单元测试,特别是并发场景下的测试。
公众号:蓝海资料掘金营,微信deep3321