3.传输低延迟
v=0 o=alice 2890844526 2890844526 IN IP4 host.anywhere.com s= c=IN IP4 host.anywhere.com t=0 0 m=audio 49170 RTP/AVP 0 a=rtpmap:0 PCMU/8000 m=video 51372 RTP/AVP 31 a=rtpmap:31 H261/90000 m=video 53000 RTP/AVP 32 a=rtpmap:32 MPV/90000WebRTC的两个端在使用RTP/SRTP传输音视频数据或使用SCTP传输data channel数据之前,需要先建立连接。建立连接的过程类似于传统电话从拨号、呼叫等待、到接通的过程。这个过程通常会有一个叫信令服务器(signaling server)的中间角色(好比文首配图的人工电话交换机)参与。而SDP在建连过程中起着重要作用,信令服务器会将两端的SDP转发给另一方,直到两端都拥有了自己和对方的会话描述信息(SDP承载),并在媒体交换格式方面达成了一致,这是两端连接成功的前提。
//webrtc-data-channel/signaling/proto/proto.go type Message struct { Cmd int `json:"command"` Payload []byte `json:"payload"` // carry all kinds of request and response }其中的Cmd字段标识Message类型,可选值如下:
//webrtc-data-channel/signaling/proto/proto.go const ( // originated from answer peer CmdInit = iota + 1 CmdAnswer // originated from answer peer CmdOffer // from both peer CmdCandidate ) const ( CmdInitResp = iota + 101 // CmdInit + 100 CmdAnswerResp CmdOfferResp CmdCandidateResp )Message既可以承载Request,亦可以承载Response。Message的Payload字段中存放的是Request或Response序列化后的结果。Request和Response结构如下:
//webrtc-data-channel/signaling/proto/proto.go // Request is one kind of payload for Message type Request struct { SourceID string `json:"source"` TargetID string `json:"target"` Body []byte `json:"body"` // carry register, offer, answer, candidate } // Request is another payload for Message type Response struct { Code int `json:"code"` Msg string `json:"msg"` }Request类型的Body中存放的是WebRTC Offer/Answer的SDP以及ICE Candidate序列化后的结果。此外,在这个示例中,我们使用WebSocket[15]来作为信令协议的载体,便于信令服务器与offer peer/answer peer进行双向通信。
//webrtc-data-channel/signaling/main.go func main() { flag.Parse() log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) http.HandleFunc("/register", register) // for peerAnswer http.HandleFunc("/offer", offer) // for peerOffer log.Fatal(http.ListenAndServe(*addr, nil)) }在这个server中我们提供了两个endpoint,一个是/register,供answer peer建立连接使用;另外一个是/offer,供offer peer与信令服务器建连并通信的。
//webrtc-data-channel/signaling/main.go func register(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) // *websocket.Conn if err != nil { log.Print("signaling: websocket upgrade error:", err) return } defer c.Close() err = answerPeerEventLoop(c, w) if err != nil { log.Println("signaling: answerPeerEventLoop error:", err) return } log.Println("signaling: answerPeerEventLoop exit") } func offer(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) // *websocket.Conn if err != nil { log.Print("signaling: websocket upgrade error:", err) return } defer c.Close() err = offerPeerEventLoop(c, w) if err != nil { log.Println("signaling: offerPeerEventLoop error:", err) return } log.Println("signaling: offerPeerEventLoop exit") }注:offer和register这两个Handler都会在单独的goroutine中执行。
$cd webrtc-data-channel/signaling $go run main.go启动answer peer:
$cd webrtc-data-channel/answer $go run main.go 2023/09/23 21:24:45.201213 answer: NewPeerConnection ok 2023/09/23 21:24:45.201256 answer: connecting to ws://localhost:18080/register 2023/09/23 21:24:45.203993 answer: recv resp[101]: proto.Response{Code:0, Msg:"ok"}这时我们会从信令服务器的输出日志中看到:
2023/09/23 21:24:45.203702 signaling: add answer peer: answer-peer-1我们看到,answer peer成功注册到信令服务器中了,其ID为answer-peer-1。
$cd webrtc-data-channel/offer $go run main.go -target answer-peer-1 2023/09/23 21:25:26.462845 offer: new peerConnection ok 2023/09/23 21:25:26.462880 offer: create new channel 2023/09/23 21:25:26.462890 offer: connecting to ws://localhost:18080/offer 2023/09/23 21:25:26.464863 offer: create offer 2023/09/23 21:25:26.465131 offer: recv resp[103]: proto.Response{Code:0, Msg:"ok"} 2023/09/23 21:25:26.465957 offer: recv answer(sdp) message from answer-peer-1 2023/09/23 21:25:26.466064 offer: set local desc 2023/09/23 21:25:26.466099 offer: set remote desc 2023/09/23 21:25:26.466201 offer: Peer Connection State has changed: connecting 2023/09/23 21:25:26.466297 offer: recv candidate message from answer-peer-1 2023/09/23 21:25:26.466344 offer: invoke peerConnection.OnICECandidate: webrtc.ICECandidate{statsID:"candidate:KsXlIk2JNeiDqK3l+znsoB3sDwuh1/2x", Foundation:"4104056053", Priority:0x7effffff, Address:"192.168.1.105", Protocol:1, Port:0xc2b1, Typ:1, Component:0x1, RelatedAddress:"", RelatedPort:0x0, TCPType:""} 2023/09/23 21:25:26.466506 offer: recv resp[104]: proto.Response{Code:0, Msg:"ok"} 2023/09/23 21:25:26.468342 offer: Peer Connection State has changed: connected 2023/09/23 21:25:26.469105 offer: Data channel 'data'-'824634439080' open. Random messages will now be sent to any connected DataChannels every 5 seconds 2023/09/23 21:25:26.859774 offer: recv candidate message from answer-peer-1 2023/09/23 21:25:31.469811 offer: Sending 'offer-1013426535' 2023/09/23 21:25:31.470846 offer: Message from DataChannel 'data': 'answer-695102175' 2023/09/23 21:25:36.469653 offer: Sending 'offer-2065047193' 2023/09/23 21:25:36.470495 offer: Message from DataChannel 'data': 'answer-750781464' 2023/09/23 21:25:41.469603 offer: Sending 'offer-153497802' 2023/09/23 21:25:41.469938 offer: Message from DataChannel 'data': 'answer-2102723687' 2023/09/23 21:25:46.469504 offer: Sending 'offer-1287609150' 2023/09/23 21:25:46.470097 offer: Message from DataChannel 'data': 'answer-645051512' 2023/09/23 21:25:51.470078 offer: Sending 'offer-1486812657' 2023/09/23 21:25:51.470572 offer: Message from DataChannel 'data': 'answer-1325372035'offer peer的启动引发了“连锁反应”,在信令服务器的帮助下,offer peer与answer peer成功建立了连接,并在打开的Data Channel进行着“定时”的双工实时通信。
2023/09/23 21:25:26.465049 signaling: recv request[3] from offer peer 2023/09/23 21:25:26.465070 signaling: send offer resp ok 2023/09/23 21:25:26.465073 signaling: add offer peer: offer-peer-1 2023/09/23 21:25:26.465085 signaling: forward request[3] to answer peer ok 2023/09/23 21:25:26.465247 signaling: recv offer response from answer peer 2023/09/23 21:25:26.465868 signaling: recv request[2] from answer peer 2023/09/23 21:25:26.465896 signaling: forward request[2] to offer peer[offer-peer-1] ok 2023/09/23 21:25:26.466003 signaling: recv answer response from offer peer 2023/09/23 21:25:26.466218 signaling: recv request[4] from answer peer 2023/09/23 21:25:26.466245 signaling: forward request[4] to offer peer[offer-peer-1] ok 2023/09/23 21:25:26.466363 signaling: recv candidate response from offer peer 2023/09/23 21:25:26.466415 signaling: recv request[4] from offer peer 2023/09/23 21:25:26.466429 signaling: send offer resp ok 2023/09/23 21:25:26.466435 signaling: add offer peer: offer-peer-1 2023/09/23 21:25:26.466445 signaling: forward request[4] to answer peer ok 2023/09/23 21:25:26.466526 signaling: recv candidate response from answer peer 2023/09/23 21:25:26.859520 signaling: recv request[4] from answer peer 2023/09/23 21:25:26.859609 signaling: forward request[4] to offer peer[offer-peer-1] ok 2023/09/23 21:25:26.859951 signaling: recv candidate response from offer peeranswer peer的输出日志如下:
2023/09/23 21:25:26.465182 answer: recv offer message from offer-peer-1 2023/09/23 21:25:26.465823 answer: send sdp answer 2023/09/23 21:25:26.465834 answer: Peer Connection State has changed: connecting 2023/09/23 21:25:26.465925 answer: set local desc 2023/09/23 21:25:26.465928 answer: recv resp[102]: proto.Response{Code:0, Msg:"ok"} 2023/09/23 21:25:26.466108 answer: invoke peerConnection.OnICECandidate: 192.168.1.105 2023/09/23 21:25:26.466285 answer: recv resp[104]: proto.Response{Code:0, Msg:"ok"} 2023/09/23 21:25:26.466481 answer: recv candidate message from offer-peer-1 2023/09/23 21:25:26.468475 answer: Peer Connection State has changed: connected 2023/09/23 21:25:26.469002 answer: New DataChannel data 824634440046 2023/09/23 21:25:26.469049 answer: Data channel 'data'-'824634440046' open. Random messages will now be sent to any connected DataChannels every 5 seconds 2023/09/23 21:25:26.859199 answer: invoke peerConnection.OnICECandidate: 175.160.224.151 2023/09/23 21:25:26.859770 answer: recv resp[104]: proto.Response{Code:0, Msg:"ok"} 2023/09/23 21:25:31.470331 answer: Sending 'answer-695102175' 2023/09/23 21:25:31.470366 answer: message from DataChannel 'data': 'offer-1013426535' 2023/09/23 21:25:36.470028 answer: Sending 'answer-750781464' 2023/09/23 21:25:36.470123 answer: message from DataChannel 'data': 'offer-2065047193' 2023/09/23 21:25:41.469624 answer: Sending 'answer-2102723687' 2023/09/23 21:25:41.469978 answer: message from DataChannel 'data': 'offer-153497802' 2023/09/23 21:25:46.469606 answer: Sending 'answer-645051512' 2023/09/23 21:25:46.469883 answer: message from DataChannel 'data': 'offer-1287609150' 2023/09/23 21:25:51.470303 answer: Sending 'answer-1325372035' 2023/09/23 21:25:51.470421 answer: message from DataChannel 'data': 'offer-1486812657'这次运行是在本地同一主机下运行的。你也可以将信令服务器搭建在公网主机上,然后将answer peer和offer peer分别放到不同的公有云虚机上,你看看是否依然可以连通!我在阿里云上的测试结果是ok的(信令服务器放在美国)。