资讯详情

构建 WebRTC 应用所需的后端服务 STUN, TURN

Build the backend services needed for a WebRTC app

STUN, TURN, and signaling 机翻

原文:Build the backend services needed for a WebRTC app: STUN, TURN, and signaling - HTML5 Rocks

WebRTC支持平等通信,但仍需要服务器,以便客户可以通过称为信令的过程交换元数据协调通信,并处理网络地址转换器(NAT)和防火墙。

本文将向您展示如何构建信令服务,如何处理和处理 STUN 和 TURN 实际连接服务器的怪癖。它还解释了WebRTC应用程序如何处理多个呼叫和服务VoIP和PSTN(也叫电话)交互。

若您不熟悉 WebRTC 阅读本文前请参考基础知识 WebRTC 入门。

什么是信号传导?

信令是协调通信的过程。WebRTC应用程序设置呼叫,其客户端需要交换以下信息:

  • 会话控制消息用于打开或关闭通信
  • 错误消息
  • 编解码器、编解码器设置、带宽和媒体类型等媒体元数据
  • 关键数据用于建立安全连接
  • 网络数据,如外界看到的主机 IP 地址和端口

这个信令过程需要一种让客户来回传递信息的方式。这种机制不是由WebRTC API实现。您需要自己构建它。在本文的后部,您将学习如何构建信令服务。但首先,你需要一些上下文。

为什么WebRTC信令没有定义?

为避免冗余,最大限度地提高与现有技术的兼容性,WebRTC该标准没有指定信令方法和协议。JavaScript 会话协议 (JSEP)总结这种方法:

WebRTC调用设置背后的想法是完全指定和控制媒体平面,但尽可能多地将信令平面留给应用程序。其基本原理是,不同的应用程序可能更喜欢使用不同的协议,如现有协议SIP或Jingle呼叫信令协议,或为特定应用程序定制的东西,可能是为了一个新的用例。在这种方法中,需要交换的关键信息是多媒体会话描述,它指定了建立媒体平面所需的必要传输和媒体配置信息。

JSEP的架构还避免了浏览器必须保存状态,即充当信令状态机。例如,如果每次重新加载页面时都会丢失信令数据,这将有问题。相反,信令状态可以保存在服务器上。

JSEP 架构

JSEP 要求在提供答案对等体之间的交换,即上述媒体元数据。折扣和答案用会话描述协议 (SDP) 格式传达如下:

<span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">v</span><span style="color:#ffffff">=</span><span style="color:#3387cc">0</span><span style="color:#ffffff"> o</span><span style="color:#ffffff">=-</span> <span style="color:#3387cc">7614219274584779017</span> <span style="color:#3387cc">2</span><span style="color:#ffffff"> IN IP4 </span><span style="color:#3387cc">127.0</span><span style="color:#ffffff">.</span><span style="color:#3387cc">0.1</span><span style="color:#ffffff"> s</span><span style="color:#ffffff">=-</span><span style="color:#ffffff"> t</span><span style="color:#ffffff">=</span><span style="color:#3387cc">0</span> <span style="color:#3387cc">0</span><span style="color:#ffffff"> a</span><span style="color:#ffffff">=</span><span style="color:#e28964">group</span><span style="color:#ffffff">:</span><span style="color:#ffffff">BUNDLE audio video a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">msid</span><span style="color:#ffffff">-</span><span style="color:#ffffff">semantic</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> WMS m</span><span style="color:#ffffff">=</span><span style="color:#ffffff">audio </span><span style="color:#3387cc">1</span><span style="color:#ffffff"> RTP</span><span style="color:#ffffff">/</span><span style="color:#ffffff">SAVPF </span><span style="color:#3387cc">111</span> <span style="color:#3387cc">103</span> <span style="color:#3387cc">104</span> <span style="color:#3387cc">0</span> <span style="color:#3387cc">8</span> <span style="color:#3387cc">107</span> <span style="color:#3387cc">106</span> <span style="color:#3387cc">105</span> <span style="color:#3387cc">13</span> <span style="color:#3387cc">126</span><span style="color:#ffffff"> c</span><span style="color:#ffffff">=</span><span style="color:#ffffff">IN IP4 </span><span style="color:#3387cc">0.0</span><span style="color:#ffffff">.</span><span style="color:#3387cc">0.0</span><span style="color:#fffff">
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">rtcp</span><span style="color:#ffffff">:</span><span style="color:#3387cc">1</span><span style="color:#ffffff"> IN IP4 </span><span style="color:#3387cc">0.0</span><span style="color:#ffffff">.</span><span style="color:#3387cc">0.0</span><span style="color:#ffffff">
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">ice</span><span style="color:#ffffff">-</span><span style="color:#ffffff">ufrag</span><span style="color:#ffffff">:</span><span style="color:#ffffff">W2TGCZw2NZHuwlnf
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">ice</span><span style="color:#ffffff">-</span><span style="color:#ffffff">pwd</span><span style="color:#ffffff">:</span><span style="color:#ffffff">xdQEccP40E</span><span style="color:#ffffff">+</span><span style="color:#ffffff">P0L5qTyzDgfmW
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">extmap</span><span style="color:#ffffff">:</span><span style="color:#3387cc">1</span><span style="color:#ffffff"> urn</span><span style="color:#ffffff">:</span><span style="color:#ffffff">ietf</span><span style="color:#ffffff">:</span><span style="color:#e28964">params</span><span style="color:#ffffff">:</span><span style="color:#ffffff">rtp</span><span style="color:#ffffff">-</span><span style="color:#ffffff">hdrext</span><span style="color:#ffffff">:</span><span style="color:#ffffff">ssrc</span><span style="color:#ffffff">-</span><span style="color:#ffffff">audio</span><span style="color:#ffffff">-</span><span style="color:#ffffff">level
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">mid</span><span style="color:#ffffff">:</span><span style="color:#ffffff">audio
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">rtcp</span><span style="color:#ffffff">-</span><span style="color:#ffffff">mux
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">crypto</span><span style="color:#ffffff">:</span><span style="color:#3387cc">1</span><span style="color:#ffffff"> AES_CM_128_HMAC_SHA1_80 </span><span style="color:#e28964">inline</span><span style="color:#ffffff">:</span><span style="color:#3387cc">9c1AHz27dZ9xPI91YNfSlI67</span><span style="color:#ffffff">/</span><span style="color:#89bdff">EMkjHHIHORiClQe</span><span style="color:#ffffff">
a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">rtpmap</span><span style="color:#ffffff">:</span><span style="color:#3387cc">111</span><span style="color:#ffffff"> opus</span><span style="color:#ffffff">/</span><span style="color:#3387cc">48000</span><span style="color:#ffffff">/</span><span style="color:#3387cc">2</span>
<span style="color:#ffffff">…</span></span></span>

想知道所有这些SDP的胡言乱语到底意味着什么吗?查看互联网工程任务组 (IETF) 示例。

请记住,WebRTC 的设计使得在将产品/服务或答案设置为本地或远程描述之前,可以通过编辑 SDP 文本中的值来调整产品/服务或答案。例如,appr.tc 中的函数可用于设置默认编解码器和比特率。使用JavaScript操纵SDP有些痛苦,并且有关于WebRTC的未来版本是否应该使用JSON的讨论,但是坚持使用SDP有一些好处。preferAudioCodec()

RTCPeerConnectionAPI 和信令:聘约、答案和候选

RTCPeerConnection是 WebRTC 应用程序用于在对等方之间创建连接以及传输音频和视频的 API。

要初始化此过程,有两个任务:RTCPeerConnection

  • 确定本地媒体条件,例如分辨率和编解码器功能。这是用于"提供和应答"机制的元数据。
  • 获取应用主机(称为候选主机)的潜在网络地址。

一旦确定了此本地数据,就必须通过信令机制与远程对等体进行交换。

想象一下,爱丽丝正试图给夏娃打电话。以下是完整的报价/答案机制的所有血腥细节:

  1. 爱丽丝创建了一个对象。RTCPeerConnection
  2. Alice 使用该方法创建一个产品/服务(SDP 会话说明)。RTCPeerConnectioncreateOffer()
  3. 爱丽丝带着她的提议打来电话。setLocalDescription()
  4. Alice 将报价串联起来,并使用信号机制将其发送给 Eve。

  5. 伊芙打电话给爱丽丝的提议,让她知道爱丽丝的设置。setRemoteDescription()RTCPeerConnection
  6. Eve 调用和对此的成功回调传递本地会话描述 — Eve 的答案。createAnswer()
  7. Eve 通过调用 将她的答案设置为本地描述。setLocalDescription()
  8. 然后,伊芙使用信号机制将她的字符串化答案发送给爱丽丝。
  9. Alice 使用 将 Eve 的答案设置为远程会话描述。setRemoteDescription()

斯特鲁特!

爱丽丝和夏娃还需要交换网络信息。"查找候选项"一词是指使用 ICE 框架查找网络接口和端口的过程。

  1. Alice 使用处理程序创建对象。RTCPeerConnectiononicecandidate
  2. 当网络候选项可用时,将调用该处理程序。
  3. 在处理程序中,Alice 通过其信令通道将字符串化的候选数据发送给 Eve。
  4. 当 Eve 收到 Alice 的应聘者消息时,她会打电话将应聘者添加到远程对等描述中。addIceCandidate()

JSEP 支持 ICE 候选滴流,它允许呼叫者在初始报价后以增量方式向被叫方提供应聘者,并允许被叫方开始对呼叫执行操作并建立连接,而无需等待所有应聘者到达。

用于信令的 WebRTC 代码

以下代码片段是一个 W3C 代码示例,它总结了完整的信令过程。该代码假定存在某种信令机制 。稍后将更详细地讨论信令。SignalingChannel

<span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// handles JSON.stringify/parse</em></span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> signaling </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">SignalingChannel</span><span style="color:#ffffff">();</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> constraints </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audio</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> video</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">};</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> configuration </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">iceServers</span><span style="color:#ffffff">:</span> <span style="color:#ffffff">[{</span><span style="color:#ffffff">urls</span><span style="color:#ffffff">:</span> <span style="color:#65b042">'stuns:stun.example.org'</span><span style="color:#ffffff">}]};</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> pc </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">configuration</span><span style="color:#ffffff">);</span>

<span style="color:#aeaeae"><em>// Send any ice candidates to the other peer.</em></span><span style="color:#ffffff">
pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onicecandidate </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">({</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">})</span> <span style="color:#ffffff">=></span><span style="color:#ffffff"> signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">});</span>

<span style="color:#aeaeae"><em>// Let the "negotiationneeded" event trigger offer generation.</em></span><span style="color:#ffffff">
pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onnegotiationneeded </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> async </span><span style="color:#ffffff">()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span>
  <span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
    await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createOffer</span><span style="color:#ffffff">());</span>
    <span style="color:#aeaeae"><em>// send the offer to the other peer</em></span><span style="color:#ffffff">
    signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">localDescription</span><span style="color:#ffffff">});</span>
  <span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
    console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span>
  <span style="color:#ffffff">}</span>
<span style="color:#ffffff">};</span>

<span style="color:#aeaeae"><em>// After remote track media arrives, show it in remote video element.</em></span><span style="color:#ffffff">
pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ontrack </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span>
  <span style="color:#aeaeae"><em>// Don't set srcObject again if it is already set.</em></span>
  <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">remoteView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject</span><span style="color:#ffffff">)</span> <span style="color:#e28964">return</span><span style="color:#ffffff">;</span><span style="color:#ffffff">
  remoteView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">streams</span><span style="color:#ffffff">[</span><span style="color:#3387cc">0</span><span style="color:#ffffff">];</span>
<span style="color:#ffffff">};</span>

<span style="color:#aeaeae"><em>// Call start() to initiate.</em></span><span style="color:#ffffff">
async </span><span style="color:#e28964">function</span><span style="color:#ffffff"> start</span><span style="color:#ffffff">()</span> <span style="color:#ffffff">{</span>
  <span style="color:#e28964">try</span> <span style="color:#ffffff">{</span>
    <span style="color:#aeaeae"><em>// Get local stream, show it in self-view, and add it to be sent.</em></span>
    <span style="color:#e28964">const</span><span style="color:#ffffff"> stream </span><span style="color:#ffffff">=</span><span style="color:#ffffff">
      await navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">(</span><span style="color:#ffffff">constraints</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
    stream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span><span style="color:#ffffff">
      pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">));</span><span style="color:#ffffff">
    selfView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">;</span>
  <span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
    console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span>
  <span style="color:#ffffff">}</span>
<span style="color:#ffffff">}</span><span style="color:#ffffff">

signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onmessage </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> async </span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> candidate</span><span style="color:#ffffff">})</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span>
  <span style="color:#e28964">try</span> <span style="color:#ffffff">{</span>
    <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span>
      <span style="color:#aeaeae"><em>// If you get an offer, you need to reply with an answer.</em></span>
      <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'offer'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
        await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">);</span>
        <span style="color:#e28964">const</span><span style="color:#ffffff"> stream </span><span style="color:#ffffff">=</span><span style="color:#ffffff">
          await navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">(</span><span style="color:#ffffff">constraints</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
        stream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span><span style="color:#ffffff">
          pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">));</span><span style="color:#ffffff">
        await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createAnswer</span><span style="color:#ffffff">());</span><span style="color:#ffffff">
        signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">localDescription</span><span style="color:#ffffff">});</span>
      <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'answer'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
        await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">);</span>
      <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
        console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Unsupported SDP type.'</span><span style="color:#ffffff">);</span>
      <span style="color:#ffffff">}</span>
    <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
      await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addIceCandidate</span><span style="color:#ffffff">(</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">);</span>
    <span style="color:#ffffff">}</span>
  <span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
    console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span>
  <span style="color:#ffffff">}</span>
<span style="color:#ffffff">};</span></span></span>

若要查看聘约/应答和候选人交换流程的实际应用,请参阅 simpl.info RTCPeerConnection,并查看控制台日志以获取单页视频聊天示例。如果您想要更多,请从Google Chrome中的 chrome://webrtc-internals 页面或Opera中的 opera://webrtc-internals 页面下载WebRTC信号和统计信息的完整转储。

对等发现

这是一种奇特的提问方式,"我如何找到可以交谈的人?

对于电话呼叫,您有电话号码和目录。对于在线视频聊天和消息传递,您需要身份和状态管理系统,以及用户启动会话的方法。WebRTC应用程序需要一种方式,让客户端相互发出信号,表明他们想要开始或加入呼叫。

WebRTC没有定义对等发现机制,您不会进入此处的选项。该过程可以像通过电子邮件发送或消息传递URL一样简单。对于视频聊天应用(如 Talky、tawk.to 和浏览器会议),您可以通过共享自定义链接来邀请他人加入通话。开发人员 Chris Ball 构建了一个有趣的无服务器 webrtc 实验,使 WebRTC 调用参与者能够通过他们喜欢的任何消息服务(如 IM、电子邮件或归巢鸽)交换元数据。

如何构建信令服务?

重申一下,WebRTC标准没有定义信令协议和机制。无论您选择什么,都需要一个中间服务器在客户端之间交换信令消息和应用程序数据。可悲的是,网络应用程序不能简单地对着互联网大喊,"把我和我的朋友联系起来! 值得庆幸的是,信号消息很小,并且主要在呼叫开始时交换。在对视频聊天会话使用 appr.tc 进行测试时,信令服务总共处理了大约 30-45 条消息,所有消息的总大小约为 10KB。

除了在带宽方面要求相对较低之外,WebRTC信令服务不会消耗太多的处理或内存,因为它们只需要中继消息并保留少量的会话状态数据,例如连接了哪些客户端。

用于交换会话元数据的信令机制也可用于传达应用数据。它只是一个消息传递服务!

将消息从服务器推送到客户端

用于信令的消息服务必须是双向的:客户端到服务器和服务器到客户端。双向通信与HTTP客户端/服务器请求/响应模型背道而驰,但是多年来已经开发了各种黑客(例如长时间轮询),以便将数据从Web服务器上运行的服务推送到在浏览器中运行的Web应用程序。

最近,EventSource API已被广泛实施。这将启用服务器发送的事件 - 通过 HTTP 从 Web 服务器发送到浏览器客户端的数据。 是为单向消息传递而设计的,但它可以与 XHR 结合使用,以构建用于交换信令消息的服务。信令服务通过 XHR 请求传递来自调用方的消息,方法是将消息推送给被调用方。EventSourceEventSource

WebSocket 是一种更自然的解决方案,专为全双工客户端-服务器通信而设计,即可以同时在两个方向上流动的消息。使用纯 WebSocket 或服务器发送的事件 () 构建的信令服务的一个优点是,这些 API 的后端可以在 PHP、Python 和 Ruby 等语言的大多数 Web 托管包通用的各种 Web 框架上实现。EventSource

除了Opera Mini之外,所有现代浏览器都支持WebSocket,更重要的是,所有支持WebRTC的浏览器也支持WebSocket,无论是在桌面还是移动设备上。TLS 应用于所有连接,以确保消息无法在未加密的情况下被截获,并减少代理遍历的问题。(有关 WebSocket 和代理遍历的更多信息,请参阅 Ilya Grigorik 的高性能浏览器网络中的 WebRTC 一章。

也可以通过让WebRTC客户端通过Ajax反复轮询消息传递服务器来处理信令,但这会导致大量冗余的网络请求,这对于移动设备来说尤其成问题。即使在建立会话后,对等方也需要轮询信号消息,以防其他对等方更改或会话终止。WebRTC Book 应用程序示例采用此选项,并对轮询频率进行了一些优化。

刻度信号

尽管信令服务每个客户端消耗的带宽和 CPU 相对较少,但常用应用的信令服务器可能必须处理来自不同位置的大量消息,并且具有很高的并发性。获得大量流量的WebRTC应用程序需要能够处理大量负载的信令服务器。

此处不详细介绍,但有许多选项可用于高容量、高性能消息传递,其中包括:

  • 可扩展消息传递和状态协议 (XMPP),最初称为 Jabber — 一种为即时消息传递开发的协议,可用于信令(服务器实现包括 ejabberd 和 Openfire。JavaScript 客户端(如 Strophe.js)使用 BOSH 来模拟双向流,但由于各种原因,BOSH 可能不如 WebSocket 高效,并且由于同样的原因,可能无法很好地扩展。(在切线上,Jingle 是用于启用语音和视频的 XMPP 扩展。WebRTC项目使用libjingle库中的网络和传输组件, 这是Jingle的C++实现。
  • 开源库,如ZeroMQ(由TokBox用于他们的Rumour服务)和OpenMQ(NullMQ将ZeroMQ概念应用于使用WEBSocket上的STOMP协议的Web平台。
  • 使用WebSocket的商业云消息传递平台(尽管它们可能会回退到长轮询),例如Pusher,Kaazing和PubNub(PubNub也有WebRTC的API)。
  • 商业 WebRTC 平台,如 vLine

(开发人员 Phil Leggetter 的《实时 Web 技术指南》提供了消息传递服务和库的完整列表。

在节点上构建具有 Socket.io 的信令服务

以下是使用 Node 上的 Socket.io 构建的信令服务的简单 Web 应用的代码。Socket.io 的设计使得构建交换消息的服务变得简单,Socket.io 由于其内置的房间概念,因此特别适合WebRTC信令。此示例并非旨在作为生产级信令服务进行扩展,但对于相对较少的用户来说,它很容易理解。

Socket.io 使用带有回退的WebSocket:AJAX长轮询,AJAX多部分流,Forever Iframe和JSONP轮询。它已被移植到各种后端,但也许最出名的是本例中使用的Node版本。

此示例中没有 WebRTC。它仅用于演示如何将信令构建到 Web 应用中。查看控制台日志,了解客户端加入聊天室和交换消息时发生的情况。这个WebRTC代码实验室提供了有关如何将其集成到完整的WebRTC视频聊天应用程序中的分步说明。

这是客户端:index.html

<span style="color:white"><span style="background-color:#444444"><span style="color:#3387cc"><!DOCTYPE html></span>
<span style="color:#89bdff"><html></span>
  <span style="color:#89bdff"><head></span>
    <span style="color:#89bdff"><title></span><span style="color:#ffffff">WebRTC client</span><span style="color:#89bdff"></title></span>
  <span style="color:#89bdff"></head></span>
  <span style="color:#89bdff"><body></span>
    <span style="color:#89bdff"><script</span> <span style="color:#bdb76b">src</span><span style="color:#ffffff">=</span><span style="color:#65b042">'/socket.io/socket.io.js'</span><span style="color:#89bdff">></script></span>
    <span style="color:#89bdff"><script</span> <span style="color:#bdb76b">src</span><span style="color:#ffffff">=</span><span style="color:#65b042">'js/main.js'</span><span style="color:#89bdff">></script></span>
  <span style="color:#89bdff"></body></span>
<span style="color:#89bdff"></html></span></span></span>

以下是客户端中引用的 JavaScript 文件:main.js

<span style="color:white"><span style="background-color:#444444"><span style="color:#e28964">const</span><span style="color:#ffffff"> isInitiator</span><span style="color:#ffffff">;</span><span style="color:#ffffff">

room </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> prompt</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Enter room name:'</span><span style="color:#ffffff">);</span>

<span style="color:#e28964">const</span><span style="color:#ffffff"> socket </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> io</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">();</span>

<span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">room </span><span style="color:#ffffff">!==</span> <span style="color:#65b042">''</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
  console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Joining room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
  socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'create or join'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">}</span><span style="color:#ffffff">

socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'full'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
  console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room </span><span style="color:#ffffff">+</span> <span style="color:#65b042">' is full'</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">});</span><span style="color:#ffffff">

socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'empty'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
  isInitiator </span><span style="color:#ffffff">=</span> <span style="color:#e28964">true</span><span style="color:#ffffff">;</span><span style="color:#ffffff">
  console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room </span><span style="color:#ffffff">+</span> <span style="color:#65b042">' is empty'</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">});</span><span style="color:#ffffff">

socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'join'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
  console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Making request to join room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
  console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'You are the initiator!'</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">});</span><span style="color:#ffffff">

socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'log'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">array</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
  console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">.</span><span style="color:#ffffff">apply</span><span style="color:#ffffff">(</span><span style="color:#ffffff">console</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> array</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">});</span></span></span>

以下是完整的服务器应用程序:

<span style="color:white"><span style="background-color:#444444"><span style="color:#e28964">const</span> <span style="color:#e28964">static</span> <span style="color:#ffffff">=</span> <span style="color:#e28964">require</span><span style="color:#ffffff">(</span><span style="color:#65b042">'node-static'</span><span style="color:#ffffff">);</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> http </span><span style="color:#ffffff">=</span> <span style="color:#e28964">require</span><span style="color:#ffffff">(</span><span style="color:#65b042">'http'</span><span style="color:#ffffff">);</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> file </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span><span style="color:#ffffff">(</span><span style="color:#e28964">static</span><span style="color:#ffffff">.</span><span style="color:#89bdff">Server</span><span style="color:#ffffff">)();</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> app </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> http</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createServer</span><span style="color:#ffffff">(</span><span style="color:#e28964">function</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">req</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> res</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
  file</span><span style="color:#ffffff">.</span><span style="color:#ffffff">serve</span><span style="color:#ffffff">(</span><span style="color:#ffffff">req</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> res</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">}).</span><span style="color:#ffffff">listen</span><span style="color:#ffffff">(</span><span style="color:#3387cc">2013</span><span style="color:#ffffff">);</span>

<span style="color:#e28964">const</span><span style="color:#ffffff"> io </span><span style="color:#ffffff">=</span> <span style="color:#e28964">require</span><span style="color:#ffffff">(</span><span style="color:#65b042">'socket.io'</span><span style="color:#ffffff">).</span><span style="color:#ffffff">listen</span><span style="color:#ffffff">(</span><span style="color:#ffffff">app</span><span style="color:#ffffff">);</span><span style="color:#ffffff">

io</span><span style="color:#ffffff">.</span><span style="color:#ffffff">sockets</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'connection'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">socket</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span>

  <span style="color:#aeaeae"><em>// Convenience function to log server messages to the client</em></span>
  <span style="color:#e28964">function</span><span style="color:#ffffff"> log</span><span style="color:#ffffff">(){</span>
    <span style="color:#e28964">const</span><span style="color:#ffffff"> array </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">[</span><span style="color:#65b042">'>>> Message from server: '</span><span style="color:#ffffff">];</span>
    <span style="color:#e28964">for</span> <span style="color:#ffffff">(</span><span style="color:#e28964">const</span><span style="color:#ffffff"> i </span><span style="color:#ffffff">=</span> <span style="color:#3387cc">0</span><span style="color:#ffffff">;</span><span style="color:#ffffff"> i </span><span style="color:#ffffff"><</span><span style="color:#ffffff"> arguments</span><span style="color:#ffffff">.</span><span style="color:#ffffff">length</span><span style="color:#ffffff">;</span><span style="color:#ffffff"> i</span><span style="color:#ffffff">++)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
      array</span><span style="color:#ffffff">.</span><span style="color:#ffffff">push</span><span style="color:#ffffff">(</span><span style="color:#ffffff">arguments</span><span style="color:#ffffff">[</span><span style="color:#ffffff">i</span><span style="color:#ffffff">]);</span>
    <span style="color:#ffffff">}</span><span style="color:#ffffff">
      socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'log'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> array</span><span style="color:#ffffff">);</span>
  <span style="color:#ffffff">}</span><span style="color:#ffffff">

  socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'message'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">message</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
    log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Got message:'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> message</span><span style="color:#ffffff">);</span>
    <span style="color:#aeaeae"><em>// For a real app, would be room only (not broadcast)</em></span><span style="color:#ffffff">
    socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">broadcast</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'message'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> message</span><span style="color:#ffffff">);</span>
  <span style="color:#ffffff">});</span><span style="color:#ffffff">

  socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">on</span><span style="color:#ffffff">(</span><span style="color:#65b042">'create or join'</span><span style="color:#ffffff">,</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span>
    <span style="color:#e28964">const</span><span style="color:#ffffff"> numClients </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> io</span><span style="color:#ffffff">.</span><span style="color:#ffffff">sockets</span><span style="color:#ffffff">.</span><span style="color:#ffffff">clients</span><span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">).</span><span style="color:#ffffff">length</span><span style="color:#ffffff">;</span><span style="color:#ffffff">

    log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room </span><span style="color:#ffffff">+</span> <span style="color:#65b042">' has '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> numClients </span><span style="color:#ffffff">+</span> <span style="color:#65b042">' client(s)'</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
    log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Request to create or join room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span>

    <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">numClients </span><span style="color:#ffffff">===</span> <span style="color:#3387cc">0</span><span style="color:#ffffff">){</span><span style="color:#ffffff">
      socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">join</span><span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
      socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'created'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span>
    <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">numClients </span><span style="color:#ffffff">===</span> <span style="color:#3387cc">1</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">
      io</span><span style="color:#ffffff">.</span><span style="color:#ffffff">sockets</span><span style="color:#ffffff">.</span><span style="color:#e28964">in</span><span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">).</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'join'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
      socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">join</span><span style="color:#ffffff">(</span><span style="color:#ffffff">room</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
      socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'joined'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span>
    <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#ffffff">{</span> <span style="color:#aeaeae"><em>// max two clients</em></span><span style="color:#ffffff">
      socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'full'</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span>
    <span style="color:#ffffff">}</span><span style="color:#ffffff">
    socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'emit(): client '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">id </span><span style="color:#ffffff">+</span>
      <span style="color:#65b042">' joined room '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> room</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
    socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">broadcast</span><span style="color:#ffffff">.</span><span style="color:#ffffff">emit</span><span style="color:#ffffff">(</span><span style="color:#65b042">'broadcast(): client '</span> <span style="color:#ffffff">+</span><span style="color:#ffffff"> socket</span><span style="color:#ffffff">.</span><span style="color:#ffffff">id </span><span style="color:#ffffff">+</span>
      <span style="color:#65b042">' joined room '</span> <span style="color:#f

标签: 2u1s9电力变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台