文章目录

  • example说明
    • 整体流程
  • 源码
    • 拆解
      • server
        • 打开本地的udp listener
        • 通过setting engine设置listener
        • 托管本地index
        • 接受client sdp的http接口
          • doSignaling
        • 启动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解析相关推荐

  1. pion webrtc 示例代码解析 一

    pion webrtc pion 是go语言写的webrtc的开发库套装 DTLS 协议 我们对DTLS 协议要适当的解释,TLS是用于TCP的,而DTLS是用于数据报的,很多应用运行在TCP之上,但 ...

  2. pion ice项目源码分析

    前言 git 地址https://github.com/pion/ice ice 流程的介绍博客https://www.rongcloud.cn/blog/?p=4178 整个源码分析会直接根据ice ...

  3. k8s四种port解析:nodePort、port、targetPort、containerPort

    1. nodePort nodePort提供了集群外部客户端访问service的一种方式,:nodePort提供了集群外部客户端访问service的端口,即nodeIP:nodePort提供了外部流量 ...

  4. snort配置文件中的PORT解析

    2019独角兽企业重金招聘Python工程师标准>>> 简介 snort读取配置文件时每条规则包括规则头和规则选项,而规则选项中的一个重要部分就是端口.snort中对端口的解析和对I ...

  5. AD9364 测试平台开发——第六篇,SPI配置内容解析

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 AD9364 测试平台开发--第六篇,SPI配置内容解析 以下为个人的一些理解,有一些东西可能不一定理解透彻了,可能有错误,请指正和见 ...

  6. SRT协议的Wireshark解析器编写(Lua)

    WireSharks插件编写(lua) 前言 API Proto ProtoField 满足按位显示的例子(同时满足字符串查找) Tvb TvbRange Pinfo TreeItem 实现协议里面添 ...

  7. Python中最好用的命令行参数解析工具

    Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要解析一个命令行参数解 ...

  8. Python中最好用的命令行解析工具:argparse

    Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要解析一个命令行参数解 ...

  9. python 命令行参数-Python 中最好用的命令行参数解析工具

    Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要一个命令行参数解析的 ...

  10. NMAP - A Stealth Port Scanner--reference

    http://nmap.org/bennieston-tutorial/ 实例:nmap -sP 192.168.21.* Contents 1  Introduction Nmap is a fre ...

最新文章

  1. Python基础20-面向对象:静态、组合、继承与派生、多态、封装、反射、内置attr方法等
  2. Qt+Phonon的另一种选择
  3. 如何强制除法为浮点数? 除数一直舍入到0?
  4. 深入理解JS中this关键字
  5. 挂在windows2003下的硬盘分区文件系统被系统识别为RAW,如何恢复至NTFS
  6. mysql数据库发布到web服务器上_web应用发布至服务器
  7. MAC终端下常用Git命令 - 某个人 - 博客园
  8. acwing2058. 笨拙的手指(进制转换)
  9. 4.4.2 数值处理
  10. 直播疑难杂症排查(1)— 播放失败
  11. 【Iphone 游戏开发之一】创建视图并绘制简单图形
  12. 2022-2028全球硬件加密行业调研及趋势分析报告
  13. 手写体识别识别(pytorch):
  14. spss多元线性回归散点图_如何通过残差散点图检验SPSS线性回归是否存在异方差等问题?...
  15. VirtualBox 磁盘扩容(亲测有效)
  16. 2018上半年最火的微信公众号有哪些?
  17. 阿里和微博的异地多活方案zt
  18. 怎么重置imac_[重置系统]如何重置Mac电脑到出厂状态
  19. Python3.7.4入门-0/1To Begin/数据类型与结构
  20. 国产数据库OpenGauss--内存优化表(MOT)实践

热门文章

  1. 阅读《人类简史》思考的几个问题。
  2. 教师节祝福短信:送给有个性的老师
  3. 关于生物医学工程{血站+软件}的看法
  4. 解决Rem的适配问题
  5. Mac电脑搞自动化浏览器总是自动化更新怎么办?看这个就可以了。
  6. android p是哪个版本,android p是什么版本
  7. 1994年颁布了计算机安全,1994年2月18日,我国颁布了( ),这是我国的第一个计算机安全法规,是我国计算机安全工作的总体纲领。...
  8. 谷歌浏览器87版本 iframe_谷歌Chrome的“混合内容”更新将会影响电商网站,自建站卖家如何应对?...
  9. 短视频三要素之封面,如何设计引人关注的封面呢?
  10. 怎么用计算机把浓度转换成PH,ph浓度换算(ph怎么换算OH浓度)