WebRTC之信令篇

在WebRTC中,信令发挥着举足轻重的作用,但是webrtc工作组并没有对信令交互进行标准化,留给开发人员自行选择。这也导致了信令交互的方案出现了多种,了解这些方案之间的差异,将有助于我们在研发WebRTC应用程序时做出正确的选择。

1 信令的作用

在实时通信中,信令的作用主要体现在以下几个方面:

  1. 协商媒体功能和设置
  2. 标识和验证会话参与者的身份
  3. 控制媒体会话、指示进度、更改会话和终止会话
  4. 当会话双方同时尝试建立或更改会话时,实施双占用分解

以上几点功能,在WebRTC中,只有第1项是必须功能,第2、3和4项均为可选功能。

1.1 为何没有建立信令标准

在WebRTC中,要让两个(web应用程序)浏览器之间能够进行互操作,无需建立标准信令。因为web服务可以确保两个浏览器通过下载同一份JavaScript代码,来实现相同的定制信令通信协议。

Web模型只对极少的组件建立了统一的标准,如下图中,服务器负责选择信令协议。并确保web应用程序或网站的各个用户支持改协议。web服务器A和B无需使用相同的信令协议,但各自都能够使用两个浏览器建立媒体会话。

需要了解的是,WebRTC是可以支持与VoIP或视频系统进行集成的,这个时候由于IP电话或视频终端为特殊终端,无法运行web应用程序。那么要实现与其互操作的唯一方式,就是支持其使用的专用信令协议,例如SIP或Jingle。

1.2 媒体协商

WebRTC规范包含了针对“信令通道”的要求。信令最重要的任务就在于,在参与对等连接的两个浏览器之间交换会话描述协议(SDP)对象中包含的信息。SDP包含供浏览器中的RTP媒体栈配置媒体会话所需要的全部信息。

  • 媒体类型(音频、视频、数据)
  • 所用的编码器(Opus、G711等)
  • 用于编解码器的各个参数或设置
  • 有关宽带信息
  • 交换候选地址,用于ICE打洞
  • 交换用于SRTP的密钥材料

1.3 标识和身份验证

使用标准信令协议(如SIP或Jingle)发起实时通信时,信令通道将提供参与者的标识,并可以选择进行身份认证。在WebRTC中,除信令之外,还有两个渠道可用于确定身份。第一种渠道是参见Web应用程序的上下文。例如一个web用户希望与另一个用户建立会话时,此web应用程序将屏幕名作为标识提供给对方,对方用户只能无条件相信对端提供的标识符可信。第二种渠道是查看URL中可能传递的标识。此URL中包含随机令牌。通过这种方式建立webrtc会话时,双方都应该知道该令牌标识。

WebRTC中定义了另外一种标识方法,即通过媒体通道来进行身份确认。在信令交互阶段。浏览器A生成自己的证书对pub-cert-A,pri-key-A,并产生公钥的指纹fgrp-A,此指纹作为SDP的一部分,随信令交互到达浏览器B。同样浏览器B也会以相同的方式,将代表自己身份的公钥指纹随信令交互传递给浏览器A。在媒体通道建立阶段,浏览器A和浏览器B开始ICE打洞,然后进行DTLS握手,建立DTLS后,此时浏览器B已经获得浏览器A的公钥证书pub-cert-A与其指纹fgrp-A,并进行验证,确认其是否匹配,从而验证浏览器A的身份确实是会话协商阶段的用户,同样浏览器A也可以对浏览器B进行验证。

1.4 控制媒体会话

传统多媒体信令协议(如SIP 或 Jingle或某种专用协议)可提供会话呼叫控制。在WebRTC中,虽然需要信令才能发起或更改媒体会话,但不需要信令来指示状态或终止会话。

1.5 双占用分解

当通信会话的双方同时尝试建立或更改会话的时候,就会出现双占用问题。SIP等信令协议内置有双占用分解功能。

2 信令传输

WebRTC信令的传输方式通常有三种:HTTP、Websocket和数据通道。

2.1 HTTP传输

HTTP也可以用于传输WebRTC信令。浏览器可发起新的HTTP请求,以便向服务器发送信令信息并从中接收信令信息。信令信息可使用GET或POST方法或已应答形式传输。如果信令服务器(注意此处之所以为信令服务器,因为信令服务器收到的HTTP请求中包含CORS相关字段,需要信令服务器进行处理)支持跨域资源共享,则其IP地址可以不同于web服务器。即web服务和HTTP信令服务可以是同一个服务,也可以是两个独立的服务。

2.2 websocket传输

WebSocket传输允许浏览器开通一个与服务器的双向连接,此连接最初采用HTTP请求的形式,但是随后升级为websocket。只要websocket服务支持CORS(Cross-Origin Resource Sharing),websocket服务器的地址可以不同于web服务器。

2.3 数据通道传输

由于两个浏览器之间建立数据通道之后,它就会提供直接的低延迟连接,这非常适合用于传输信令。但是由于最初数据通道建立时需要单独的信令机制,因此数据通道无法单独用于传输所有的webrtc信令。

3 信令协议

WebRTC信令协议的选择至关重要,而且不必局限于所选的信令传输方式。开发人员可选择创建自己的专有信令协议,采用 SIP 或 Jingle 等标准信令协议,或者使用通过抽象化处理剥离了信令协议细节的库。

采用专有信令协议的优点在于,它可以非常简单,并且只有提供应用程序所需的功能。如果 WebRTC 对等连接始终仅限于两个浏览器,而不通过中间环节或不通向 SIP 或 Jingle VoIP 或者视频终端,则适合采取这一选项。

下面我们主要介绍一下可能用到的两种自定义信令的方式,对于专有信息协议(如SIP或Jingle)就不进行详细介绍了。

3.1 信令标识

为实现标识和验证会话参与者的身份的作用,需要在服务器中设置某种路由逻辑。如果浏览器和服务器之间的给定连接可以通过令牌标识,则发送至服务器的信令消息可包含另一个“服务器至浏览器”连接的令牌。这样,Web服务器代码将在两个连接之间充当代理或用于转发信息。

3.2 HTTP轮询

HTTP轮询是一种简单的专有信令方案,通过在 JavaScript 或 jQuery 中调用 XHR,可使 JavaScript 应用程序针对Web服务器生成新的HTTP请求并处理HTTP响应。XHR 是一种W3C标准API,XHR JavaScript API 的各个功能组件均受浏览器支持,虽然 XHR 的名称中包含 HTTP请求,但除了发送 XML 请求之外,它还可以发送 JSON 或 明文。XHR 可以使浏览器生成新的 HTTP 或 HTTPS 请求,例如 GET、PUT、POST等,相应的 API 调用将指定要使用的方法以及IP地址和端口号。针对请求的响应将会被返回给 JavaScript。

要使用 XHR 作为 WebRTC 信令通道,Web服务器需要运行相应的应用程序,用于通过另一个XHR通道接收HTTP请求并从一个浏览器接收信息以代理的形式转发给另外一个浏览器。

为了交换信令信息。每个浏览器中运行的JavaScript会定期向信令服务器发起HTTP消息轮询,浏览器使用POST方法发送信息消息,服务器收到的信令信息包含在发送给POST的200 OK响应中。(请注意,此处HTTP请求均为短连接,每一个消息都是一个新的请求。)

3.3 WebSocket代理

对于用来传输 WebRTC 信令的 WebSockets 代理,其使用的服务器将具有公共的IP地址,两个对等连接的服务器均可以访问。每个浏览器都与 websocket代理服务器建立一个连接,代理服务器进行消息的转发。

3.4 信令协议总结

方案服务器要求优点
WebSocket代理提供服务器代码的websocket服务器无需信令基础架构
XML HTTP提供服务器代码的web服务器无需基础信令架构
SIP支持SIP websocket传输的 SIP 注册/ 代理服务器易与SIP终端或基础架构互操作,无需无服务器代码
Jingle支持 XMPP websocket传输的 XMPP服务器易与Jingle终端或基础架构互操作,无需服务器代码
数据通道用于建立数据通道的websocket或web服务器信令延迟短并可以保护信令隐私

4 websocket信令服务器示例

此处使用js库 socket.io

var log4js = require('log4js');
var http = require('http');
var https = require('https');
var fs = require('fs');
var socketIo = require('socket.io');

var express = require('express');
var serveIndex = require('serve-index');

var USERCOUNT = 3;

log4js.configure({
    appenders: {
        file: {
            type: 'file',
            filename: 'app.log',
            layout: {
                type: 'pattern',
                pattern: '%r %p - %m',
            }
        }
    },
    categories: {
       default: {
          appenders: ['file'],
          level: 'debug'
       }
    }
});

var logger = log4js.getLogger();

var app = express();
app.use(serveIndex('./public'));
app.use(express.static('./public'));



//http server
var http_server = http.createServer(app);
http_server.listen(80, '0.0.0.0');

var options = {
    key : fs.readFileSync('./public/cert/server-key.pem'),
    cert: fs.readFileSync('./public/cert/server-cert.pem')
}

//https server
var https_server = https.createServer(options, app);
var io = socketIo.listen(https_server);

//服务端收到连接后的处理函数
io.sockets.on('connection', (socket)=> {

/*处理此连接上的 message 类型的消息*/
    socket.on('message', (room, data)=>{
        logger.debug('message, room: ' + room + ", data, type:" + data.type);
        socket.to(room).emit('message',room, data);
    });


/*处理此连接上的 join 类型的消息*/
    socket.on('join', (room)=>{
        socket.join(room);
        var myRoom = io.sockets.adapter.rooms[room]; 
        var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
        logger.debug('the user number of room (' + room + ') is: ' + users);

        if(users < USERCOUNT){
            socket.emit('joined', room, socket.id); //发给除自己之外的房间内的所有人
            if(users > 1){
                socket.to(room).emit('otherjoin', room, socket.id);
            }
        
        }else{
            socket.leave(room); 
            socket.emit('full', room, socket.id);
        }
        //socket.emit('joined', room, socket.id); //发给自己
        //socket.broadcast.emit('joined', room, socket.id); //发给除自己之外的这个节点上的所有人
        //io.in(room).emit('joined', room, socket.id); //发给房间内的所有人
    });

/*处理此连接上的 leave 类型的消息*/
    socket.on('leave', (room)=>{

        socket.leave(room);

        var myRoom = io.sockets.adapter.rooms[room]; 
        var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
        logger.debug('the user number of room is: ' + users);

        //socket.emit('leaved', room, socket.id);
        //socket.broadcast.emit('leaved', room, socket.id);
        socket.to(room).emit('bye', room, socket.id);
        socket.emit('leaved', room, socket.id);
        //io.in(room).emit('leaved', room, socket.id);
    });

});

https_server.listen(443, '0.0.0.0');
console.log("start singal");
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值