sequenceDiagram
participant ClientA
participant SignalingServer
participant ClientB
participant STUN/TURN
participant Internet
%% 1. 信号分离
Note over ClientA: 1. 信令协商
ClientA->>ClientA: PC = new RTCPeerConnection()
ClientA->>ClientA: pc.addTrack(localStream)
ClientA->>ClientA: offer = pc.createOffer()
ClientA->>ClientA: pc.setLocalDescription(offer)
ClientA->>SignalingServer: SEND Offer SDP
SignalingServer->>ClientB: FORWARD Offer
ClientB->>ClientB: pc.setRemoteDescription(offer)
ClientB->>ClientB: answer = pc.createAnswer()
ClientB->>ClientB: pc.setLocalDescription(answer)
ClientB->>SignalingServer: SEND Answer SDP
SignalingServer->>ClientA: FORWARD Answer
ClientA->>ClientA: pc.setRemoteDescription(answer)
%% 2. ICE候选交换
Note over ClientA, ClientB: 2. ICE候选交换
loop A收集候选
ClientA->>STUN/TURN: GET srflx候选
STUN/TURN-->>ClientA: 返回公网IP:Port
ClientA->>SignalingServer: SEND ICE Candidate
end
loop B收集候选
ClientB->>STUN/TURN: GET srflx候选
STUN/TURN-->>ClientB: 返回公网IP:Port
ClientB->>SignalingServer: SEND ICE Candidate
end
SignalingServer->>ClientB: FORWARD A的候选
SignalingServer->>ClientA: FORWARD B的候选
ClientA->>ClientA: pc.addIceCandidate(B候选)
ClientB->>ClientB: pc.addIceCandidate(A候选)
%% 3. 媒体和绑定
Note over ClientA,ClientB: 3. 媒体流绑定
ClientA->>ClientA: pc.ontrack = (e) => {绑定B的流}
ClientB->>ClientB: pc.ontrack = (e) => {绑定A的流}
%% 4. 连接建立
Note over ClientA,ClientB: 4. 连接建立
ClientA->>ClientB: STUN Binding Request
ClientB->>ClientA: STUN Binding Response
alt P2P成功
ClientA->>ClientB: DTLS Handshake
else 失败
ClientA->>STUN/TURN: Use TURN Relay
end
ClientA->>ClientB: SRTP Media Channel UP
%% 5. 数据传输
Note over ClientA,ClientB: 5. 数据传输
ClientA->>ClientB: 发送媒体流(VP8/Opus)
ClientB->>ClientA: 发送媒体流(H.264/G.711)
ClientA->>ClientB: dataChannel.send()
ClientB->>ClientA: dataChannel.onmessage()