WebRTC中的信令和内网穿透技术 STUN / TURN

WebRTC中的信令和内网穿透技术 STUN / TURNTranslatedfromWebRTCintherealworld:STUN,TURNandsignaling.最近刚接触到WebRTC,网上看到这篇介绍WebRTC的文章不错,仔细读了读还算有用,分享出来能帮到一些刚入门的人也挺好的,翻译不好的地方可以直接看原文。WebRTC可以进行P2P点对点通信,但是WebRTC仍然需要服务器:客户端需要服务器交换一些数据来协…

大家好,又见面了,我是你们的朋友全栈君。

Translated from WebRTC in the real world: STUN, TURN and signaling.
最近刚接触到WebRTC,网上看到这篇介绍WebRTC的文章不错,仔细读了读还算有用,分享出来能帮到一些刚入门的人也挺好的,翻译不好的地方可以直接看原文。

WebRTC可以进行P2P点对点通信,但是WebRTC仍然需要服务器:

  • 客户端需要服务器交换一些数据来协调通信,这称之为信令。
  • 使用服务器来应对NAT网络地址转换和防火墙。

在本文中,将介绍如何构建信令服务,以及如何使用STUN和TURN服务器来处理WebRTC在实际使用过程中的连接问题。本文还将解释WebRTC应用程序如何处理多方通话,并与诸如VoIP和PSTN(AKA电话)之类的服务进行交互。
如果您不熟悉WebRTC的基本知识,我们强烈建议您在阅读本文之前先看一下如何开始使用WebRTC。

什么是信令?

信令用于协调通信,WebRTC应用开始通话之前,客户端需要交换一些信息(信令):

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

客户端之间来回传递这些消息需要实现一种信令通信方式,但是WebRTC的API并没有实现信令通信机制,所以使用者需要自己去实现。下面会介绍一些构建信令服务的方法,但是这里可以先了解一下这些背景。

WebRTC为什么不规定信令标准?

为了避免冗余并提高与已有技术的兼容性,WebRTC标准未规定信令方法和协议。JavaScript会话建立协议(JSEP)描述了一种大致的方法:

WebRTC设计思想是完全指定和控制媒体层面,把信令层面尽可能的交给应用去实现。这是因为不同的应用程序可能更喜欢使用不同的信令协议,比如已经存在的SIP或者Jingle信令协议,抑或一些针对应用定制的协议。在这种方法中,需要交换的关键信息是多媒体会话描述,它指定了建立媒体连接所必需的传输和媒体配置信息。

JSEP的体系结构使浏览器不必保存状态:也就是说,作为一个信令状态机,如果在每次重新加载页面时丢失信令数据,这将是有问题的。相反,可以在服务器上保存信令状态。

JSEP 架构
image

JSEP需要在 offer / 提议answer / 应答 的点与点之间交换上文提到的媒体元数据信息。交换信息的两个点之间使用SDP会话描述协议进行通信。SDP协议消息格式大概是这个样子:

v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
…

想查看SDP官方文档,点这里 SDP for the WebRTC

WebRTC被设计成可以通过修改一些SDP文本中的值来调整会话,使用JavaScript操作SDP有点麻烦,也有讨论WebRTC的未来版本是否应该使用JSON代替SDP,但目前因为使用这个方法还有一些优点所以坚持使用SDP。

RTCPeerConnection + 信令:offer(提议)、answer(应答)和candidate(候选地址)

这几个词翻译过来也不好理解,算了不翻译了。还有那个P2P的peer就先翻译为端点吧,总不能直接说是个P。

RTCPeerConnection是WebRTC应用程序在点对点之间创建连接并传送音频和视频的API。
要想创建音视频通信连接,RTCPeerConnection有两个任务:

  • 确定本地媒体信息,例如分辨率和编解码器信息。这是用于offer和answer机制的元数据。
  • 获取应用程序主机的网络地址,称为candidate。

一旦确定了本地数据,就必须通过信令机制与远程端点的进行交换。
假如有这么一个场景,Alice尝试与Eve进行通话,下面是完整的 offer / answer 机制的细节:

  1. Alice创建了一个RTCPeerConnection对象。
  2. Alice使用RTCPeerConnectioncreateOffer()方法创建了一个offer(一个SDP会话描述文本)。
  3. Alice调用setLocalDescription()将她的offer设置为本地描述。
  4. Alice把offer转换为字符串,并使用信令机制将其发送给Eve。
  5. Eve对Alice的offer调用setRemoteDescription()函数,为了让他的RTCPeerConnection知道Alice的设置。
  6. Eve调用createAnswer()函数创建answer
  7. Eve通过调用setLocalDescription()将她的answer设置为本地描述。
  8. Eve使用信令机制把她字符串化的的answer传给Alice。
  9. Alice使用setRemoteDescription()函数将Eve的answer设置为远程会话描述。

Alice和Eve也需要去交换网络信息。“查找候选地址candidate”一词是指使用ICE框架查找网络接口和端口的过程。

  1. Alice创建RTCPeerConnection对象的时候会生成一个onicecandidate句柄。
  2. 这个句柄在网络candidate生效时会被调用。
  3. Alice通过信令通道将字符串化的candidate数据发送给Eve。
  4. 当Eve从Alice获取candidate消息时,她调用addIceCandidate(),将candidate添加到远程对等描述中。

JSEP支持ICE Candidate Trickling,它允许调用方在初始化 offer 之后递增地向被调用方提供候选地址candidate,并且允许被调用方在没有等待所有候选地址candidate到达的情况下开始进行操作并建立连接。

关于信令的WebRTC代码

下面这段代码总结了信令的完整过程,这段代码假定存在SignalingChannel信令机制。后面会详细讨论信令。

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = { 
   audio: true, video: true};
const configuration = { 
   iceServers: [{ 
   urls: 'stuns:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// 给另外一个端点发送candidate
pc.onicecandidate = ({ 
   candidate}) => signaling.send({ 
   candidate});

// 让"negotiationneeded"事件触发生成offer
pc.onnegotiationneeded = async () => { 
   
  try { 
   
    await pc.setLocalDescription(await pc.createOffer());
    // send the offer to the other peer
    signaling.send({ 
   desc: pc.localDescription});
  } catch (err) { 
   
    console.error(err);
  }
};

// 一旦远程媒体到达,就把它放在远程视频元素结构中
pc.ontrack = (event) => { 
   
  // don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// 调用 start() 进行初始化
async function start() { 
   
  try { 
   
    // 获取本地流,把它显示在本地视频窗口中并发送出去
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) { 
   
    console.error(err);
  }
}

signaling.onmessage = async ({ 
   desc, candidate}) => { 
   
  try { 
   
    if (desc) { 
   
      // 如果收到一个offer,就需要响应一个answer
      if (desc.type === 'offer') { 
   
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({ 
   desc: pc.localDescription});
      } else if (desc.type === 'answer') { 
   
        await pc.setRemoteDescription(desc);
      } else { 
   
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) { 
   
      await pc.addIceCandidate(candidate);
    }
  } catch (err) { 
   
    console.error(err);
  }
};

这个网站simpl.info/pc提供一个WebRTC视频聊天的示例程序,可以在这页面直观感受一下视频聊天的过程(电脑需要有摄像头并且允许浏览器使用)。如果你想查看视频对话的过程中offer/answercandidate的交互过程log,可以从下面的页面查看或者下载一个完整的WebRTC信令和统计表格:Chrome浏览器进入这个页面chrome://webrtc-internals,Opera浏览器进入这个页面opera://webrtc-internals。(先打开前面的视频对话的网页开启视频对话,然后打开后面的地址可以查看详细交互信息)。

Peer Discovery / 对点发现机制

这是一种奇特的说法 – 我如何找人交谈?
对于打电话,我们有电话号码或者查询号码簿。对于在线视频聊天和消息传递,我们需要身份和状态管理系统,以及用户启动会话的方法。WebRTC应用程序需要一种方法让客户向他们想要发起或加入会议的其他人发送信号。

WebRTC没有规定对点发现机制,该过程可以像通过电子邮件发送URL一样简单。视频聊天应用可以把每个会议用一个URL进行表示,参加会议的人通过点击这个URL就可以进行视频会议了。开发人员Chris Ball构建了一个有趣的无服务器WebRTC测试,使WebRTC参会者能够通过他们喜欢的任何消息服务交换元数据,例如IM,电子邮件等。

如何建立信令服务?

注意!WebRTC标准没有定义信令协议和机制。
无论您选择哪种实现方式,您都需要一个中间服务器来在客户端之间交换信令消息和应用程序数据。因为在一个网络应用程序不能简单地向互联网喊“把我连接到我的朋友”就可以连接的。(歪果仁的脑回路确实清奇)
值得庆幸的是,信令消息通常很小,并且主要在呼叫开始时进行交换。在使用appr.tc进行测试时发现,对于视频聊天会话,信令服务总共处理了大约30-45条消息,所有消息的总大小也就10kB左右。
WebRTC信令服务不仅带宽占用得少,而且使用的内存资源等也都非常少,因为他只需要中继消息并保留少量的会话状态数据(例如连接的客户端)。

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

用于信令的消息服务应该是双向的:客户端到服务器和服务器到客户端。这种双向通信违背了HTTP C/S 请求/响应模型,但是为了将数据从Web服务器推送到浏览器应用上,多年来已经开发了诸如长轮询之类的技术

最近, EventSource API已经得到广泛应用。这这个API启用了“server-sent events”:通过HTTP从Web服务器连续向浏览器客户端发送数据。EventSource是为单向消息传递而设计的,但是它可以与XHR结合使用,以构建用于交换信令消息的服务:信令服务通过将消息通过EventSource推送到被调用方,从调用方传递由XHR请求传递的消息。

WebSocket是一种更自然的解决方案,就是为了全双工的客户端-服务器通信(消息可以同时双向流动)而设计的。使用纯WebSocket或Server-Sent Events(EventSource)构建的信号服务的一个优点是,这些API的后端可以使用PHP、Python和Ruby等语言,可以在大多数常用的Web框架上实现。

目前,大约四分之三的浏览器支持WebSocket,更重要的是,无论是在桌面还是移动设备上,支持WebRTC的所有浏览器也支持WebSocket。所有的链接都应该使用TLS以确保不被拦截到未加密的消息,还可以减少代理的遍历问题。

WebRTC视频聊天应用程序 “appR.TC”的信令是通过Google App Engine Channel API实现的,该API使用Comet技术(长轮询)在App Engine后端和Web客户端之间进行推送信令。

也可以通过WebRTC客户端多次使用AJAX轮询消息服务器来处理信令,但这会导致大量冗余的网络请求,特别是对于移动设备而言更严重。即使在一个会话已经建立,节点也需要在其他节点发生变化或终止会话的情况下轮询信令消息。

扩展信令

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

这里不会详细介绍针对高容量高性能的消息传递处理方法,仅仅列出如下几种选择:

  • XMPP(可扩展消息传递和呈现协议):为即时消息传递开发的可用于信令的协议。
  • 开源库,如ZeroMQ和OpenMQ。NullMQ使用基于WebSocket的STOMP协议将ZeroMQ概念应用于Web平台。
  • 使用WebSocket的商业云消息传递平台,例如Pusher,Kaazing和PubNub。
  • 商业WebRTC平台,如vLine。

(开发者Phil Leggetter的实时Web技术指南提供了消息服务和库的综合列表。)

在Node上使用Socket.io构建信令服务

下面是一个简单的Web应用程序的代码,它使用在Node上使用Socket.io构建的信令服务。Socket.io的设计使构建交换消息的服务变得简单,而Socket.io特别适合WebRTC信令,因为它内置了“房间”的概念。此示例不是为生产级信令服务而设计的,但对于相对少量的用户来说很容易理解。

Socket.io使用带有AJAX长轮询、AJAX多部分流、Forever Iframe和JSONP轮询机制的WebSocket。它已被移植到各种后端,但可能其Node版本是最有名的,我们在下面的示例中使用它。

在这个例子中没有WebRTC:它的设计只是为了展示如何在Web应用程序中构建信令。查看控制台日志以查看客户端加入会议室并交换消息时发生了什么。我们的WebRTC代码库提供了如何将其集成到完整的WebRTC视频聊天应用程序中的详细说明。

下面是客户端index.html代码。

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/socket.io/socket.io.js'></script>
    <script src='js/main.js'></script>
  </body>
</html>

下面是客户端引用的JavaScript文件main.js

const isInitiator;

room = prompt('Enter room name:');

const socket = io.connect();

if (room !== '') { 
   
  console.log('Joining room ' + room);
  socket.emit('create or join', room);
}

socket.on('full', (room) => { 
   
  console.log('Room ' + room + ' is full');
});

socket.on('empty', (room) => { 
   
  isInitiator = true;
  console.log('Room ' + room + ' is empty');
});

socket.on('join', (room) => { 
   
  console.log('Making request to join room ' + room);
  console.log('You are the initiator!');
});

socket.on('log', (array) => { 
   
  console.log.apply(console, array);
});

完整的服务器APP

const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) { 
   
  file.serve(req, res);
}).listen(2013);

const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => { 
   

  // convenience function to log server messages to the client
  function log(){ 
   
    const array = ['>>> Message from server: '];
    for (const i = 0; i < arguments.length; i++) { 
   
      array.push(arguments[i]);
    }
      socket.emit('log', array);
  }

  socket.on('message', (message) => { 
   
    log('Got message:', message);
    // for a real app, would be room only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', (room) => { 
   
    const numClients = io.sockets.clients(room).length;

    log('Room ' + room + ' has ' + numClients + ' client(s)');
    log('Request to create or join room ' + room);

    if (numClients === 0){ 
   
      socket.join(room);
      socket.emit('created', room);
    } else if (numClients === 1) { 
   
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room);
    } else { 
    // max two clients
      socket.emit('full', room);
    }
    socket.emit('emit(): client ' + socket.id +
      ' joined room ' + room);
    socket.broadcast.emit('broadcast(): client ' + socket.id +
      ' joined room ' + room);

  });

});

要想运行这个app,你需要先安装Node、socket.io和node-static。从Node.js网站下载相应版本Node进行安装,然后使用一下命令安装另外两个库。

npm install socket.io
npm install node-static

运行node server.js命令来启动服务器。这时打开浏览器访问localhost:2013,然后再打开一个页面访问此地址,模拟两个独立的客户端。可以使用Command-Option-J(Mac)或Ctrl-Shift-J(Win)命令来查看浏览器此时处理过程。

无论选择何种方式发送信令,你的服务器后端和客户端应用程序至少都需要提供类似于此示例的服务。

信令陷阱

  • 在调用setLocalDescription()之前,RTCPeerConnection不会收集candidates信息。
  • 利用Trickle ICE机制(见上文):candidates到达后立即调用addIceCandidate()

现成的信令服务器

如果你不想自己动手实现信令服务器,这有几个使用了Socket.io的、与客户端JavaScript库集成WebRTC信令服务器可以使用:

如果您根本不想编写任何代码,可以从vLine,OpenTok和Asterisk等公司获得完整的商业WebRTC平台解决方案。

信令安全

所有WebRTC组件都必须加密。但是,WebRTC标准并未定义信令机制,因此你需要想办法确保信令安全。如果攻击者设法劫持信令,他们可以停止会话,重定向连接并记录,更改或注入内容。

确保信令的最重要因素是使用安全协议、HTTPS和WSS(例如TLS),确保不能被拦截到未加密的消息。也要注意,不要以相同的信令服务器访问其他信令者的方式来广播信令消息。

事实上,为了保护WebRTC应用程序,信令使用TLS绝对是必要的。

使用ICE处理NAT和防火墙

对于元数据信令,WebRTC应用程序使用中间服务器,但是对于实际的媒体和数据流,一旦建立会话,RTCPeerConnection就会尝试点对点直接连接客户端。

简单网络结构中,每个WebRTC端点都有一个唯一的地址,可以直接与其他端点交换信息直接通信。

没有NAT和防火墙的应用场景如下图
A world without NATs and firewalls

实际上,大多数设备都处于一层或多层NAT网络结构中,有些设备具有阻止某些端口和协议的防病毒软件,而且许多设备都支持代理和企业防火墙。防火墙和NAT也可以由相同的设备实现,例如家庭wifi路由器。

真实的使用场景
NAT

WebRTC应用程序可以使用ICE框架来克服现实网络的复杂性。要实现此目的,您的应用程序必须将ICE服务器URL传递给RTCPeerConnection,如下所述。

ICE会尝试遍历两个端点之间的所有路径并查找最佳路径。ICE首先尝试使用从设备的操作系统和网卡获得的主机地址建立连接。如果这个方法失败(表示此时设备处于NAT环境下),ICE使用STUN服务器获取外部地址。如果使用STUN也无法连接,则通过TURN中继服务器进行路由。

换句话说:

  • STUN服务器用于获取外部网络地址。
  • 如果直连失败,TURN服务器将用于中继流量。

每个TURN服务器都支持STUN:TURN服务器是内置了中继功能的STUN服务器。ICE还可以应对复杂的NAT设置,实际上,NAT打洞可能不仅仅需要共有IP和端口。

WebRTC应用的 RTCPeerConnection 构造函数的第一个参数 iceServers 中会指定STUN或TURN服务器的URL。对于appr.tc,该值看起来像这样:

{ 
   
  'iceServers': [
    { 
   
      'urls': 'stun:stun.l.google.com:19302'
    },
    { 
   
      'urls': 'turn:192.158.29.39:3478?transport=udp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    },
    { 
   
      'urls': 'turn:192.158.29.39:3478?transport=tcp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    }
  ]
}

注意:上面显示的TURN证书示例是有时间限制的,并于2013年9月到期。要测试凭据,您可以使用candidate采集示例程序并检查您是否获得了中继类型的candidate。

一旦RTCPeerConnection具有该信息,RTCPeerConnection就可以使用ICE框架计算出端点之间的最佳路径,必要时会使用STUN和TURN服务器。

STUN

NAT为设备提供内网IP地址,以便在专用本地网络中使用,但是这个地址不能在外部使用。对于WebRTC而言,没有公共地址,点与点之间就无法直接进行通信。为了解决这个问题,WebRTC采用STUN技术。

STUN服务器位于公网上并且有一个简单的任务:检查传入请求的IP和端口地址(来自在NAT网络中运行的应用程序)并将该地址作为响应发回。换句话说,应用程序使用STUN服务器查询其位于公网上的IP和端口。此过程使WebRTC端点能够查询到自己公开访问的地址,然后通过信令机制将其传递给另一个端点,以便建立直接链接。(事实上,不同的NAT以不同的方式工作,并且可能存在多个NAT层,但原理仍然是相同的)。

大白话的说就是,很多内网的设备可以给公网地址发送数据,并不知道公网是用什么的地址来识别自己的,STUN服务器在收到查询请求的时候会告诉这个设备它的公网地址是啥样子的。设备拿到这个地址把这个地址发送给需要建立直接联系的其他设备

STUN服务器对计算性能和存储要求都不太高,因此相对低规格的STUN服务器可以处理大量请求。

根据webrtcstats.com的统计,有86%的WebRTC应用使用STUN成功建立连接,在内网端点之间的呼叫可能会更少,因为不用考虑防火墙和NAT地址转换。

使用STUN服务器去获取本设备公共的地址
Using STUN servers to get public IP:port addresses

TURN

RTCPeerConnection尝试通过UDP建立点与点之间的直接通信。如果失败,RTCPeerConnection将转向TCP。如果TCP连接失败,可以将TURN服务器用作回退,在端点之间中继数据。

注意:TURN用于在端点之间中继音频/视频/数据流,而不是信令数据!

TURN服务器具有公共地址,因此即使端点位于防火墙或代理之后,也可以与其他端点进行通信。TURN服务器虽然只有这么一个简单的任务 —— 中继流, 但与STUN服务器不同,它们本身就消耗了大量带宽。换句话说,TURN服务器需要更强大。

完整的交互过程: STUN, TURN 和信令图
The full Monty: STUN, TURN and signaling
此图显示TURN正在运行:单纯使用STUN未成功连接,因此每个端点都使用TURN服务器进行中继。

部署STUN和TURN服务器

为了进行测试,Google运行appr.tc使用的是公共STUN服务器stun.l.google.com:19302。对于生产STUN / TURN服务,我们建议使用rfc5766-turn-server。

可以从code.google.com/p/rfc5766-turn-server获取STUN和TURN服务器的源代码,该代码还提供了有关服务器安装的多个信息源的链接。还提供Amazon Web Services的VM映像。

restund是一个可替代的TURN服务器,这个代码也是开源的,也可用于AWS。以下是如何在Google Compute Engine上设置restund的介绍:

  1. 根据需要打开防火墙相应端口,tcp=443,udp/tcp=3478。
  2. 创建具有公网IP的四个实例,标准Ubuntu 12.04映像。
  3. 设置本地防火墙配置 (allow ANY from ANY)。
  4. 安装必要的工具makegcc
  5. 安装库creytiv.com/re.html.
  6. 下载restund并解压, creytiv.com/restund.html
  7. 执行wget hancke.name/restund-auth.patchpatch -p1 < restund-auth.patch
  8. 执行 make, sudo make install安装libre和restund。
  9. 根据需要调整restund.conf(替换IP地址并确保它包含相同的共享密钥)并复制到/etc
  10. restund/etc/restund复制到/etc/init.d/
  11. 配置restund:
    • Set LD_LIBRARY_PATH
    • Copy restund.conf to /etc/restund.conf
    • Set restund.conf to use the right 10. IP address
  12. 运行restund。
  13. 从远程机器测试这个stund: ./client IP:port

具有多个端点的WebRTC

上面讨论的都是一对一的呼叫,很容易想象,媒体流的用例不仅仅是简单的一对一呼叫。比如一群同事一起组织一个会议或者需要众多人观看的会议都是多个端点同时在线的。

WebRTC应用程序可以使用多个RTCPeerConnections,以便每个端点连接到网状配置中的每个其他端点。这是talky.io等应用程序采用的方法,这种每个端点都直接连接的方式对于少数几个参会者系统来说的话效果非常好。但是这种方式处理和带宽消耗变得过大,尤其是对于移动客户端。

Mesh拓扑结构: 每个端点都直接连接

Full mesh topology: everyone connected to everyone

除此之外,WebRTC应用程序可以选择一个端点,以星形网络配置将流分发给所有其他端点。也可以直接在服务器上运行一个WebRTC端点(虚拟参会者)并构建自己的重新分发机制。

从Chrome 31和Opera 18开始,一个RTCPeerConnectionMediaStream可以作为另一个RTCPeerConnection的输入。这样可以实现更灵活的架构,因为它允许Web应用程序通过选择要连接的其他端点来处理呼叫路由。

MCU / 多点控制单元

对于拥有大量端点而言,更好的选择是使用多点控制单元(MCU),这是一个可以作为在大量参与者之间分发媒体数据的类似于桥梁的服务器。MCU可以调整视频会议不同分辨率,编解码器和帧速率,处理转码,进行选择性流转发以及混合或记录音频和视频。对于多方通话,需要考虑许多问题:特别是如何显示多个视频输入并混合来自多个来源的音频。vLine等云平台也会尝试优化流量路由。

可以购买完整的MCU硬件,也可以自己构建。
有几种开源MCU软件可供选择。例如,Licode为WebRTC生产开源MCU; 或者OpenTok的Mantis

浏览器之外的VoIP,电话和消息

浏览器中运行的WebRTC应用程序可能需要与在另一通信平台(例如电话或视频会议系统)上运行的设备或平台之间建立通信,WebRTC的标准化特性使这种情况成为可能。

SIP协议是VoIP和视频会议系统使用的信令协议。为了实现WebRTC Web应用程序与SIP客户端(如视频会议系统)之间的通信,WebRTC需要一个代理服务器来调解信令。信令必须通过网关,但是一旦建立了通信,SRTP流量(视频和音频)就可以在端点之间直连了。

PSTN,公共交换电话网,是老式模拟电话的电路交换网络。对于WebRTC Web应用程序和电话之间的呼叫,流量必须通过PSTN网关。同样,WebRTC Web应用程序需要中间XMPP服务器与Jingle端点(如IM客户端)进行通信。Jingle是由Google开发的XMPP扩展,目的是为语音和视频提供消息传递服务:当前的WebRTC实现是基于C++ libjingle库的,这是最初为Google Talk开发的Jingle实现版本。

许多应用程序、库和平台利用WebRTC特性,比如:

  • sipML5: 一个开源的JavaScript SIP客户端。
  • jsSIP: JavaScript SIP库
  • Phono: 作为插件存在的开源的JavaScript phone API。
  • Zingaya: 一个可嵌入的手机小部件。
  • Twilio: 语音和消息通信。
  • Uberconference: 会议。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/160563.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • activexobject对象不能创建_无法创建office组件对象

    activexobject对象不能创建_无法创建office组件对象JavaScript中ActiveXObject对象是启用并返回Automation对象的引用。使用方法:newObj=newActiveXObject(servername.typename[,location])ActiveXObject对象语法有这些部分:其中newObj是必选项。要赋值为ActiveXObject的变量名。1.servername是必选项。提供该对象的…

    2022年10月15日
  • python之for循环详解_python循环5次

    python之for循环详解_python循环5次目录1、可以明确循环的次数2、iteratle_object(可迭代对象)3、enumerate()使用方法为4、补充:统计文件行数可以这样写:1、可以明确循环的次数遍历一个数据集内的成员 在列表解析中使用 生成器表达式中使用2、iteratle_object(可迭代对象)String(字符串) List(列表) Tuple(元组) Dictionary…

  • Linux关闭防火墙命令

    Linux关闭防火墙命令一、下面是redhat/CentOs7关闭防火墙的命令!1:查看防火状态systemctlstatusfirewalldserviceiptablesstatus2:暂时关闭防火墙systemctlstopfirewalldserviceiptablesstop3:永久关闭防火墙systemctldisablefirewalldchkc…

  • java 怎么更新session值_java – 如何更新session.setAttribute(name,value)值,其中名称相同?…「建议收藏」

    java 怎么更新session值_java – 如何更新session.setAttribute(name,value)值,其中名称相同?…「建议收藏」我有一种情况,我需要更新名称保持不变的setAttribute的值.考虑以下情况作为示例–假设我有三个JSP:abc.jsp,xyz.jsp,pqr.jsp.现在首先运行abc.jsp然后控制前进到xyz.jsp&然后转发到pqr.jsp.现在执行pqr.jspt后,再次使用setAttribute中的更新值控制回xyz.jsp.abc.jsp:ArrayListgetSupplyS…

    2022年10月16日
  • jquery获取iframe的src(input标签type属性有哪些)

    一句搞定,不搞那些花里胡哨的$(‘#InformationURL’).attr(‘src’,’https://www.baidu.com’);//#InformationURL:iframe的id=”InformationURL”,注意这里的#是后加上的//这里将src设置为百度,当然你也可以设置为参数形式//其他的为固定写法…

  • ssm框架过时了吗_spring实战

    ssm框架过时了吗_spring实战SpringSpring是一个开源的免费的框架Spring是一个轻量级的,非入侵式的框架控制反转(IOC),面向切面编程(AOP)支持事务的处理,对框架整合的支持IOC理论UserDaoUserDaoImpUserSeviceUserServiceImp在之前,用户的需求可能会影响原来的代码。使用一个set。public void setUserDao(UserDao userDao){ this.userDao = userDao;}之前是主动创建对象,控制

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号