文章目录
- example说明
- 源码
- 拆解
- server
- 打开本地的udp listener
- 通过setting engine设置listener
- 托管本地index
- 接受client sdp的http接口
- 启动http服务
- client
- 创建datachannel
- 打印远端candidate信息
- 创建本地sdp,发送给服务器,并设置服务器sdp
example说明
这个example是一个用来展示功能:可以在相同的udp端口上连接不同的客户端PeerConnection,每个客户端的PeerConnection的端口是客户端自己生成的,服务器的端口是最开始就确定好的,并且被所有的客户端生成。
此外,这个example还模拟了通过datachannel做基本的数据收发。有一个相对比较完整的流程,可以作为入门参考。
整体流程
#mermaid-svg-sasy9Jdt338CCqld .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-sasy9Jdt338CCqld .label text{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .node rect,#mermaid-svg-sasy9Jdt338CCqld .node circle,#mermaid-svg-sasy9Jdt338CCqld .node ellipse,#mermaid-svg-sasy9Jdt338CCqld .node polygon,#mermaid-svg-sasy9Jdt338CCqld .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-sasy9Jdt338CCqld .node .label{text-align:center;fill:#333}#mermaid-svg-sasy9Jdt338CCqld .node.clickable{cursor:pointer}#mermaid-svg-sasy9Jdt338CCqld .arrowheadPath{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-sasy9Jdt338CCqld .flowchart-link{stroke:#333;fill:none}#mermaid-svg-sasy9Jdt338CCqld .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-sasy9Jdt338CCqld .edgeLabel rect{opacity:0.9}#mermaid-svg-sasy9Jdt338CCqld .edgeLabel span{color:#333}#mermaid-svg-sasy9Jdt338CCqld .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-sasy9Jdt338CCqld .cluster text{fill:#333}#mermaid-svg-sasy9Jdt338CCqld div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-sasy9Jdt338CCqld .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-sasy9Jdt338CCqld text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-sasy9Jdt338CCqld .actor-line{stroke:grey}#mermaid-svg-sasy9Jdt338CCqld .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-sasy9Jdt338CCqld .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-sasy9Jdt338CCqld #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-sasy9Jdt338CCqld .sequenceNumber{fill:#fff}#mermaid-svg-sasy9Jdt338CCqld #sequencenumber{fill:#333}#mermaid-svg-sasy9Jdt338CCqld #crosshead path{fill:#333;stroke:#333}#mermaid-svg-sasy9Jdt338CCqld .messageText{fill:#333;stroke:#333}#mermaid-svg-sasy9Jdt338CCqld .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-sasy9Jdt338CCqld .labelText,#mermaid-svg-sasy9Jdt338CCqld .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-sasy9Jdt338CCqld .loopText,#mermaid-svg-sasy9Jdt338CCqld .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-sasy9Jdt338CCqld .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-sasy9Jdt338CCqld .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-sasy9Jdt338CCqld .noteText,#mermaid-svg-sasy9Jdt338CCqld .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-sasy9Jdt338CCqld .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-sasy9Jdt338CCqld .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-sasy9Jdt338CCqld .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-sasy9Jdt338CCqld .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .section{stroke:none;opacity:0.2}#mermaid-svg-sasy9Jdt338CCqld .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-sasy9Jdt338CCqld .section2{fill:#fff400}#mermaid-svg-sasy9Jdt338CCqld .section1,#mermaid-svg-sasy9Jdt338CCqld .section3{fill:#fff;opacity:0.2}#mermaid-svg-sasy9Jdt338CCqld .sectionTitle0{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .sectionTitle1{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .sectionTitle2{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .sectionTitle3{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-sasy9Jdt338CCqld .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .grid path{stroke-width:0}#mermaid-svg-sasy9Jdt338CCqld .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-sasy9Jdt338CCqld .task{stroke-width:2}#mermaid-svg-sasy9Jdt338CCqld .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .taskText:not([font-size]){font-size:11px}#mermaid-svg-sasy9Jdt338CCqld .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-sasy9Jdt338CCqld .task.clickable{cursor:pointer}#mermaid-svg-sasy9Jdt338CCqld .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-sasy9Jdt338CCqld .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-sasy9Jdt338CCqld .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-sasy9Jdt338CCqld .taskText0,#mermaid-svg-sasy9Jdt338CCqld .taskText1,#mermaid-svg-sasy9Jdt338CCqld .taskText2,#mermaid-svg-sasy9Jdt338CCqld .taskText3{fill:#fff}#mermaid-svg-sasy9Jdt338CCqld .task0,#mermaid-svg-sasy9Jdt338CCqld .task1,#mermaid-svg-sasy9Jdt338CCqld .task2,#mermaid-svg-sasy9Jdt338CCqld .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-sasy9Jdt338CCqld .taskTextOutside0,#mermaid-svg-sasy9Jdt338CCqld .taskTextOutside2{fill:#000}#mermaid-svg-sasy9Jdt338CCqld .taskTextOutside1,#mermaid-svg-sasy9Jdt338CCqld .taskTextOutside3{fill:#000}#mermaid-svg-sasy9Jdt338CCqld .active0,#mermaid-svg-sasy9Jdt338CCqld .active1,#mermaid-svg-sasy9Jdt338CCqld .active2,#mermaid-svg-sasy9Jdt338CCqld .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-sasy9Jdt338CCqld .activeText0,#mermaid-svg-sasy9Jdt338CCqld .activeText1,#mermaid-svg-sasy9Jdt338CCqld .activeText2,#mermaid-svg-sasy9Jdt338CCqld .activeText3{fill:#000 !important}#mermaid-svg-sasy9Jdt338CCqld .done0,#mermaid-svg-sasy9Jdt338CCqld .done1,#mermaid-svg-sasy9Jdt338CCqld .done2,#mermaid-svg-sasy9Jdt338CCqld .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-sasy9Jdt338CCqld .doneText0,#mermaid-svg-sasy9Jdt338CCqld .doneText1,#mermaid-svg-sasy9Jdt338CCqld .doneText2,#mermaid-svg-sasy9Jdt338CCqld .doneText3{fill:#000 !important}#mermaid-svg-sasy9Jdt338CCqld .crit0,#mermaid-svg-sasy9Jdt338CCqld .crit1,#mermaid-svg-sasy9Jdt338CCqld .crit2,#mermaid-svg-sasy9Jdt338CCqld .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-sasy9Jdt338CCqld .activeCrit0,#mermaid-svg-sasy9Jdt338CCqld .activeCrit1,#mermaid-svg-sasy9Jdt338CCqld .activeCrit2,#mermaid-svg-sasy9Jdt338CCqld .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-sasy9Jdt338CCqld .doneCrit0,#mermaid-svg-sasy9Jdt338CCqld .doneCrit1,#mermaid-svg-sasy9Jdt338CCqld .doneCrit2,#mermaid-svg-sasy9Jdt338CCqld .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-sasy9Jdt338CCqld .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-sasy9Jdt338CCqld .milestoneText{font-style:italic}#mermaid-svg-sasy9Jdt338CCqld .doneCritText0,#mermaid-svg-sasy9Jdt338CCqld .doneCritText1,#mermaid-svg-sasy9Jdt338CCqld .doneCritText2,#mermaid-svg-sasy9Jdt338CCqld .doneCritText3{fill:#000 !important}#mermaid-svg-sasy9Jdt338CCqld .activeCritText0,#mermaid-svg-sasy9Jdt338CCqld .activeCritText1,#mermaid-svg-sasy9Jdt338CCqld .activeCritText2,#mermaid-svg-sasy9Jdt338CCqld .activeCritText3{fill:#000 !important}#mermaid-svg-sasy9Jdt338CCqld .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-sasy9Jdt338CCqld g.classGroup text .title{font-weight:bolder}#mermaid-svg-sasy9Jdt338CCqld g.clickable{cursor:pointer}#mermaid-svg-sasy9Jdt338CCqld g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-sasy9Jdt338CCqld g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-sasy9Jdt338CCqld .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-sasy9Jdt338CCqld .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-sasy9Jdt338CCqld .dashed-line{stroke-dasharray:3}#mermaid-svg-sasy9Jdt338CCqld #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld .commit-id,#mermaid-svg-sasy9Jdt338CCqld .commit-msg,#mermaid-svg-sasy9Jdt338CCqld .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-sasy9Jdt338CCqld g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-sasy9Jdt338CCqld g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-sasy9Jdt338CCqld g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-sasy9Jdt338CCqld g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-sasy9Jdt338CCqld .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-sasy9Jdt338CCqld .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-sasy9Jdt338CCqld .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-sasy9Jdt338CCqld .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-sasy9Jdt338CCqld .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-sasy9Jdt338CCqld .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-sasy9Jdt338CCqld .edgeLabel text{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-sasy9Jdt338CCqld .node circle.state-start{fill:black;stroke:black}#mermaid-svg-sasy9Jdt338CCqld .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-sasy9Jdt338CCqld #statediagram-barbEnd{fill:#9370db}#mermaid-svg-sasy9Jdt338CCqld .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-sasy9Jdt338CCqld .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-sasy9Jdt338CCqld .statediagram-state .divider{stroke:#9370db}#mermaid-svg-sasy9Jdt338CCqld .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-sasy9Jdt338CCqld .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-sasy9Jdt338CCqld .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-sasy9Jdt338CCqld .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-sasy9Jdt338CCqld .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-sasy9Jdt338CCqld .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-sasy9Jdt338CCqld .note-edge{stroke-dasharray:5}#mermaid-svg-sasy9Jdt338CCqld .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-sasy9Jdt338CCqld .error-icon{fill:#522}#mermaid-svg-sasy9Jdt338CCqld .error-text{fill:#522;stroke:#522}#mermaid-svg-sasy9Jdt338CCqld .edge-thickness-normal{stroke-width:2px}#mermaid-svg-sasy9Jdt338CCqld .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-sasy9Jdt338CCqld .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-sasy9Jdt338CCqld .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-sasy9Jdt338CCqld .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-sasy9Jdt338CCqld .marker{fill:#333}#mermaid-svg-sasy9Jdt338CCqld .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-sasy9Jdt338CCqld {color: rgba(0, 0, 0, 0.75);font: ;}serverclient监听udp端口,并设置给RTC Engine启动http文件服务器,托管index网页创建peer connect,获取local sdpclient sdpserver sdpset server sdpsend message, timestampprint timestamp from remoteserverclient
index.html网页中会在网页打开的时候,创建peer connection,获取client sdp,然后给server发消息,获取server sdp;监听远程的消息
为了简化说明,server sdp指的是服务器自己产生的sdp,client sdp指的是客户端自己产生的sdp
源码
源码地址:https://github.com/pion/webrtc/tree/master/examples/ice-single-port
拆解
server
打开本地的udp listener
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IP{0, 0, 0, 0}, Port: 8443,
})
if err != nil { panic(err)
}
通过setting engine设置listener
// Create a SettingEngine, this allows non-standard WebRTC behavior
// 创建SettingEngine,通过SettingEnginen,能够允许非标准的WebRTC流程,比如说固定ice的端口
settingEngine := webrtc.SettingEngine{} // Configure our SettingEngine to use our UDPMux. By default a PeerConnection has
// no global state. The API+SettingEngine allows the user to share state between them.
// In this case we are sharing our listening port across many.
// 设置UDPMux,即UDP复用器,允许不同的peer connection共享相同的端口
settingEngine.SetICEUDPMux(webrtc.NewICEUDPMux(nil, udpListener)) // Create a new API using our SettingEngine
// 使用settingEngine创建webrtc api。后续可以通过这个api完成双向通信
// 这个api是个全局变量,可以在接收到远端的sdp offer之后调用处理
api = webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine))
托管本地index
http.Handle("/", http.FileServer(http.Dir(".")))
这里的pwd目录下面有一个index.html文件,里面就是一会会说到的client的逻辑,在web sdk中非常有代表性
接受client sdp的http接口
这里的doSignaling接口实现的就是一个最简单的webrtc 信令服务器。传送了双方建立连接所需要的最基本的信息
http.HandleFunc("/doSignaling", doSignaling)
官方示例中没有给出这个协议的具体内容,这里做一个补充
request
POST /doSignaling HTTP1.1{"type": "offer","sdp": "<sdp string>"
}
response
{"type": "answer","sdp": "<sdp string>"
}
doSignaling
简单说来,就是利用之前创建的webrtc api,和收到的sdp,创建peerConnection,等待客户端的ice通道建立完成,然后将自己的sdp送给客户端
func doSignaling(w http.ResponseWriter, r *http.Request) { peerConnection, err := api.NewPeerConnection(webrtc.Configuration{}) if err != nil { panic(err) } // Set the handler for ICE connection state // This will notify you when the peer has connected/disconnected peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String()) }) // Send the current time via a DataChannel to the remote peer every 3 seconds peerConnection.OnDataChannel(func(d *webrtc.DataChannel) { d.OnOpen(func() { for range time.Tick(time.Second * 3) { if err = d.SendText(time.Now().String()); err != nil { panic(err) } } }) }) var offer webrtc.SessionDescription if err = json.NewDecoder(r.Body).Decode(&offer); err != nil { panic(err) } if err = peerConnection.SetRemoteDescription(offer); err != nil { panic(err) } // Create channel that is blocked until ICE Gathering is complete gatherComplete := webrtc.GatheringCompletePromise(peerConnection) answer, err := peerConnection.CreateAnswer(nil) if err != nil { panic(err) } else if err = peerConnection.SetLocalDescription(answer); err != nil { panic(err) } // Block until ICE Gathering is complete, disabling trickle ICE// we do this because we only can exchange one signaling message// in a production application you should exchange ICE Candidates via OnICECandidate<-gatherComplete response, err := json.Marshal(*peerConnection.LocalDescription()) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") if _, err := w.Write(response); err != nil { panic(err) }
}
启动http服务
panic(http.ListenAndServe(":8080", nil))
同时开启
- index.html托管服务
- 接受client sdp的http接口服务
client
创建datachannel
let pc = new RTCPeerConnection()
let dc = pc.createDataChannel('data')
打印远端candidate信息
dc.onopen = () => { let el = document.createElement('template') let selectedPair = pc.sctp.transport.iceTransport.getSelectedCandidatePair() el.innerHTML = `<div> <ul> <li> <i> Local</i> - ${selectedPair.local.candidate}</li> <li> <i> Remote</i> - ${selectedPair.remote.candidate} </li> </ul> </div>`document.getElementById('iceSelectedPairs').appendChild(el.content.firstChild);
}
创建本地sdp,发送给服务器,并设置服务器sdp
pc.createOffer() .then(offer => { pc.setLocalDescription(offer) return fetch(`/doSignaling`, { method: 'post', headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, body: JSON.stringify(offer) }) }) .then(res => res.json()) .then(res => pc.setRemoteDescription(res)) .catch(alert)
【pion】ice-single-port解析相关推荐
- pion webrtc 示例代码解析 一
pion webrtc pion 是go语言写的webrtc的开发库套装 DTLS 协议 我们对DTLS 协议要适当的解释,TLS是用于TCP的,而DTLS是用于数据报的,很多应用运行在TCP之上,但 ...
- pion ice项目源码分析
前言 git 地址https://github.com/pion/ice ice 流程的介绍博客https://www.rongcloud.cn/blog/?p=4178 整个源码分析会直接根据ice ...
- k8s四种port解析:nodePort、port、targetPort、containerPort
1. nodePort nodePort提供了集群外部客户端访问service的一种方式,:nodePort提供了集群外部客户端访问service的端口,即nodeIP:nodePort提供了外部流量 ...
- snort配置文件中的PORT解析
2019独角兽企业重金招聘Python工程师标准>>> 简介 snort读取配置文件时每条规则包括规则头和规则选项,而规则选项中的一个重要部分就是端口.snort中对端口的解析和对I ...
- AD9364 测试平台开发——第六篇,SPI配置内容解析
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 AD9364 测试平台开发--第六篇,SPI配置内容解析 以下为个人的一些理解,有一些东西可能不一定理解透彻了,可能有错误,请指正和见 ...
- SRT协议的Wireshark解析器编写(Lua)
WireSharks插件编写(lua) 前言 API Proto ProtoField 满足按位显示的例子(同时满足字符串查找) Tvb TvbRange Pinfo TreeItem 实现协议里面添 ...
- Python中最好用的命令行参数解析工具
Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要解析一个命令行参数解 ...
- Python中最好用的命令行解析工具:argparse
Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要解析一个命令行参数解 ...
- python 命令行参数-Python 中最好用的命令行参数解析工具
Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要一个命令行参数解析的 ...
- NMAP - A Stealth Port Scanner--reference
http://nmap.org/bennieston-tutorial/ 实例:nmap -sP 192.168.21.* Contents 1 Introduction Nmap is a fre ...
最新文章
- Python基础20-面向对象:静态、组合、继承与派生、多态、封装、反射、内置attr方法等
- Qt+Phonon的另一种选择
- 如何强制除法为浮点数? 除数一直舍入到0?
- 深入理解JS中this关键字
- 挂在windows2003下的硬盘分区文件系统被系统识别为RAW,如何恢复至NTFS
- mysql数据库发布到web服务器上_web应用发布至服务器
- MAC终端下常用Git命令 - 某个人 - 博客园
- acwing2058. 笨拙的手指(进制转换)
- 4.4.2 数值处理
- 直播疑难杂症排查(1)— 播放失败
- 【Iphone 游戏开发之一】创建视图并绘制简单图形
- 2022-2028全球硬件加密行业调研及趋势分析报告
- 手写体识别识别(pytorch):
- spss多元线性回归散点图_如何通过残差散点图检验SPSS线性回归是否存在异方差等问题?...
- VirtualBox 磁盘扩容(亲测有效)
- 2018上半年最火的微信公众号有哪些?
- 阿里和微博的异地多活方案zt
- 怎么重置imac_[重置系统]如何重置Mac电脑到出厂状态
- Python3.7.4入门-0/1To Begin/数据类型与结构
- 国产数据库OpenGauss--内存优化表(MOT)实践
热门文章
- 阅读《人类简史》思考的几个问题。
- 教师节祝福短信:送给有个性的老师
- 关于生物医学工程{血站+软件}的看法
- 解决Rem的适配问题
- Mac电脑搞自动化浏览器总是自动化更新怎么办?看这个就可以了。
- android p是哪个版本,android p是什么版本
- 1994年颁布了计算机安全,1994年2月18日,我国颁布了( ),这是我国的第一个计算机安全法规,是我国计算机安全工作的总体纲领。...
- 谷歌浏览器87版本 iframe_谷歌Chrome的“混合内容”更新将会影响电商网站,自建站卖家如何应对?...
- 短视频三要素之封面,如何设计引人关注的封面呢?
- 怎么用计算机把浓度转换成PH,ph浓度换算(ph怎么换算OH浓度)