WebRTC 只管流不管控——自研信令服务器的状态机设计视频流是 WebRTC 的事。谁发起、谁接听、谁踢人、谁旁观——这些是信令的事。一、问题WebRTC 搞定了音视频传输。两个浏览器之间怎么建 PeerConnection、怎么传递 SDP、怎么走 ICE 打洞——这些都是现成的。但一个视频帮办系统不只是两个人能看见对方。还需要坐席发起通话参保人接听坐席可以邀请第三方加入旁观坐席可以踢人多方加入时每个人知道房间里有哪些人有人断线其他人要知道通话结束后知道是谁挂断的、挂了多久这些 WebRTC 不管。得自己搭一层信令服务器。二、自研信令协议设计一套轻量 JSON 信令。WebSocket 传输每条消息一个类型信令方向说明谁来谁收create坐席创建房间成功服务器坐席joined有人加入房间后给此人发的确认带了所有在房间的人列表服务器加入者called有人申请加入通知坐席是否允许服务器坐席allow坐席允许加入坐席服务器deny坐席拒绝加入坐席服务器refused服务器通知申请者被拒绝服务器申请者otherjoin有其他人加入房间通知给已经在房间里的人服务器其他人bye有人主动离开离开者服务器leaved有人离开服务器通知房间里其他人服务器其他人kick坐席踢人坐席服务器kicked被踢的人收到通知服务器被踢者关键设计决策所有业务操作不走 P2P全部经过信令服务器中转。坐席踢人不是直接发给参保人是发给服务器 → 服务器发给被踢者 通知其他人。单一控制点状态不会分叉。三、房间状态机一个房间的生命周期┌──────────┐ │ idle │ 等待坐席创建 └────┬─────┘ │ create ▼ ┌──────────┐ │ created │ 房间已建等参保人加入 └────┬─────┘ │ join ▼ ┌──────────┐ │ joined │ 参保人已加入等坐席允许 └────┬─────┘ │ allow ▼ ┌──────────┐ │ called │ 通话中可能多人 └────┬─────┘ │ bye / kick / timeout ▼ ┌──────────┐ │ ended │ 所有人离开房间销毁 └──────────┘每个状态允许的操作不同状态允许的操作created参保人加入、坐席取消joined坐席允许/拒绝、第三方加入called踢人、邀请第三方、正常挂断状态机保护了异常路径——比如坐席在created状态还没人加入就发踢人指令服务器直接拒绝不会崩溃。四、三种角色的加入和退出逻辑坐席建房发create→ 服务器创建房间分配 roomId退出发bye→ 服务器通知所有人leaved→ 房间销毁参保人加入发join带 roomId→ 服务器暂存 → 发called给坐席被允许服务器发joined→ 参保人开始建 P2P 连接被拒绝服务器发refused→ 参保人收到拒绝原因被踢坐席发kick→ 服务器发kicked给参保人 → 通知其他人leaved第三方旁观加入同参保人但坐席收到called时能看到是旁观角色退出发bye或坐席踢逻辑同参保人差别不参与音视频流只收到其他人的存在通知五、一次完整通话的信令时序以坐席 → 参保人一对一通话为例坐席 信令服务器 参保人 │ │ │ │──────── create ────────▶│ │ │◀───── create OK ────────│ │ │ │ │ ◀────── join ───────│ │◀───── called ───────────│ │ │ │ │──────── allow ──────────▶│ │ │◀───── allow OK ──────────│ │ │ │────── otherjoin ────────▶│ │ │ │ │════════════ WebRTC P2P 音视频流 ═══════════════════│ │ │ │ │ ◀─────── bye ───────│ │◀───── leaved ────────────│ │Notes:坐席建房后房间处于created状态等待参保人。参保人 join 后状态变为joined坐席决定 allow 还是 deny。allow 后状态变为called视频流开始。参保人主动挂断bye服务器通知坐席leaved房间结束。如果是坐席主动 bye服务器直接销毁房间通知所有人 leaved。六、异常处理断线、超时、重复加入断线WebSocket 断连时服务器检测到连接关闭等同于该用户主动 bye。处理逻辑走正常离开流程通知其他人leaved保留房间 N 分钟等待重连超时没重连的人从房间列表移除超时joined状态等待坐席 allow 超过 N 分钟 → 服务器自动给参保人发refused房源回退到created。重复加入同一个 roomId同一个 userId 重复发 join → 服务器忽略返回已在房间中。七、总结WebRTC 搞定了音视频流的传输。但它不负责谁有权建房谁有权加入谁有权踢人旁观和发言有什么区别断线了怎么处理这些是信令服务器的活。这套自研协议只有 10 条左右信令覆盖了远程帮办的全部业务场景。核心设计原则是所有状态变更必须经过服务器——不是性能最优的方案但状态一致性最高。政务场景不需要百万人并发但绝对不能在通话中丢了状态。信令层的代码不复杂。复杂的是想清楚每个状态下谁可以做什么。这个想清楚了代码是自然推导出来的。