老成员 A ↔ 信令服务器 ↔ 新成员 B,含 STUN/TURN 媒体面打洞过程
sequenceDiagram
participant A as 老成员 A
participant S as 信令服务器 (Socket.IO)
participant T as STUN/TURN (UDP)
participant B as 新成员 B
rect rgba(30,60,120,0.15)
Note over A,S: ① 信令连接(各自与服务器)
A->>S: Engine.IO 握手 (EIO)
S-->>A: clientPeerConfig {forceRelayOnly, iceServers}
B->>S: Engine.IO 握手
S-->>B: clientPeerConfig {forceRelayOnly, iceServers}
end
rect rgba(30,120,60,0.15)
Note over B,S: ② 进房(游戏状态触发)
B->>S: leave → id(身份) → join(lobbyCode, isHost)
S-->>B: setClients(成员表) + setHost
S-->>A: join(B的socketId, client)
end
rect rgba(140,90,20,0.15)
Note over A,B: ③ SDP 协商(A 是 initiator,经服务器中转)
A->>S: signal {to:B, type:offer, sdp, connectionId}
S->>B: signal {from:A, offer}
B->>S: signal {to:A, type:answer, sdp}
S->>A: signal {from:B, answer}
A->>S: signal {candidate} ×N
S->>B: signal {candidate} ×N
B->>S: signal {candidate} ×N
S->>A: signal {candidate} ×N
end
rect rgba(120,30,120,0.15)
Note over A,T: ④ 收集 + 打洞(媒体面,与服务器无关)
A->>T: STUN Binding
T-->>A: srflx (公网地址)
A->>T: TURN Allocate
T-->>A: relay (中继地址)
A->>B: 连通性检查 (STUN Binding ×候选对)
B->>A: 连通性检查
Note over A,B: 打洞成功 = P2P 直连
双方对称 NAT = relay 对,媒体过 T
end
rect rgba(30,120,120,0.15)
Note over A,B: ⑤ 语音运行中
A<<-->>B: WebRTC 音频流 + DataChannel(P2P,房主经通道下发房间设置)
A->>S: VAD(talking)
S-->>B: VAD {activity, client, socketId}
S-->>A: setClients / setHost / setClient(成员与房主变更)
end