WebRTC基础实践 - 7. 配置信令服务

本节内容

在本节课程中, 我们将学习以下内容:

  • 通过 npm 安装 package.json 文件中指定的项目依赖
  • 运行Node.js服务器, 通过 node-static 提供静态文件服务。
  • 用Socket.IO创建消息传递服务
  • 创建聊天室以及发送聊天消息。

本节的完整版代码位于 step-04 文件夹中。

基本概念

要创建并保持WebRTC通话, 客户端之间需要互相交换元数据信息, 包括:

  • 候选网络信息(Candidate);
  • 媒介相关的邀请信息(Offer)和响应信息(answer), 比如分辨率(resolution), 编解码器(codec)等。

换句话说, 想要传输流媒体视频/数据, 必须得先互相交换元数据信息。这个过程被称为信令传输(signaling)。

在前面的小节中, 发送方和接收方都是同一个页中的 RTCPeerConnection 对象, 所以传递信令只需要在对象间直接拷贝就行, 显得特别简单, 。

在现实世界中, 发送方和接收方一般是不同的设备, 所以需要具有元数据交换的通道。

我们可以使用信令服务器(signaling server), 来为WebRTC客户端(peers)之间传递消息。实际上这些信令消息都是纯文本格式的, 也就是将JavaScript对象序列化为字符串的形式(stringified)。

环境准备: 安装Node.js

要运行本节和接下来的示例代码(从step-04step-06), 需要在本机安装 Node.js。

Node.js中文网下载链接: http://nodejs.cn/download/;

当然也可以直接从Node.js官网下载: https://nodejs.org/en/download/

某些平台上可以通过包管理器进行安装, 请参考: https://nodejs.org/en/download/package-manager/

安装完成后, 在项目路径下, 执行命令 npm install 安装相关的依赖, 然后可以通过命令 node index.js 来启动本地服务器。稍后会在必要时介绍这些命令。

app简介

WebRTC使用客户端方式的JavaScript API, 在实际应用中, 需要有信令服务器(消息服务)的支持, 有时还需要使用 STUN 和 TURN 服务器。 更多信息请参考: https://www.html5rocks.com/en/tutorials/webrtc/infrastructure/

在本节课程中, 我们先创建简单的 Node.js 信令服务器, 使用 Socket.IO 模块和JavaScript库来传递消息。 如果你熟悉Node.js和Socket.IO, 会比较容易理解; 如果不熟悉也没关系; 消息组件的使用非常简单。

选择正确的信令服务器

本教程使用 Socket.IO 作为信令服务器。

基于Socket.IO的设计, 将其用作消息服务简单又直接。 Socket.IO 非常适合用于学习WebRTC信令, 因为其内置了 “聊天室”(rooms) 这个概念。

当然, 对于商用级产品来说, 还有很多更好的选择。 请参考 How to Select a Signaling Protocol for Your Next WebRTC Project

在本示例中, 通过Node.js服务器启动 index.js 文件, 客户端的实现位于 index.html 文件中。

在本节中, Node.js程序做了两件事情。

一、 作为消息中继服务器:

socket.on('message', function (message) {
  log('Got message: ', message);
  socket.broadcast.emit('message', message);
});

二、 管理WebRTC视频聊天室:

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

这是个简单的WebRTC应用, 每个房间只支持两个客户端。

HTML和JavaScript代码

更新 index.html 文件, 内容如下:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>
  <script src="/socket.io/socket.io.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

页面上没有太多东西: 所有的日志信息都在浏览器控制台输出。(要打开Chrome控制台, 可以使用快捷键 Ctrl-Shift-J, 或 F12, Mac系统则是 Command-Option-J)。

替换 js/main.js 文件的内容:

'use strict';

var isInitiator;

window.room = prompt("Enter room name:");

var socket = io.connect();

if (room !== "") {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room, clientId) {
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});

socket.on('ipaddr', function(ipaddr) {
  console.log('Message from client: Server IP address is ' + ipaddr);
});

socket.on('joined', function(room, clientId) {
  isInitiator = false;
});

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

设置 Socket.IO

在HTML文件中, 可以看到, 我们使用了一个 Socket.IO 的文件:

<script src="/socket.io/socket.io.js"></script>

work目录中创建文件: package.json, 其内容如下:

{
  "name": "webrtc-codelab",
  "version": "0.0.1",
  "description": "WebRTC codelab",
  "dependencies": {
    "node-static": "^0.7.10",
    "socket.io": "^1.2.0"
  }
}

这就是一个应用清单文件, 主要是告知Node包管理器(npm, Node Package Manager)需要安装的依赖项。

要安装依赖, (比如我们使用的 /socket.io/socket.io.js ), 可以在 work 目录下执行命令:

npm install

如果是在国内, 可以使用 cnpm,

首先需要全局安装 cnpm:

npm install -g cnpm

然后才能使用cnpm, cnpm用法和npm完全一致:

cnpm install

然后可以看到相关的日志信息.

省略。。。

可以看到, npm 安装了 package.json 中定义的依赖项。

work 目录下创建一个新的文件index.js, 内容如下:

注意服务端脚本不放到 js 目录中

'use strict';

var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');

var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
  fileServer.serve(req, res);
}).listen(8080);

var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {

  // convenience function to log server messages on the client
  function log() {
    var array = ['Message from server:'];
    array.push.apply(array, arguments);
    socket.emit('log', array);
  }

  socket.on('message', function(message) {
    log('Client said: ', message);
    // for a real app, would be room-only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', function(room) {
    log('Received request to create or join room ' + room);

    var clientsInRoom = io.sockets.adapter.rooms[room];
    var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;

    log('Room ' + room + ' now has ' + numClients + ' client(s)');

    if (numClients === 0) {
      socket.join(room);
      log('Client ID ' + socket.id + ' created room ' + room);
      socket.emit('created', room, socket.id);

    } else if (numClients === 1) {
      log('Client ID ' + socket.id + ' joined room ' + room);
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room, socket.id);
      io.sockets.in(room).emit('ready');
    } else { // max two clients
      socket.emit('full', room);
    }
  });

  socket.on('ipaddr', function() {
    var ifaces = os.networkInterfaces();
    for (var dev in ifaces) {
      ifaces[dev].forEach(function(details) {
        if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
          socket.emit('ipaddr', details.address);
        }
      });
    }
  });

});

打开命令行终端, 在work目录下执行命令:

node index.js

也可以将这个命令保存为启动脚本, 如 startup_index.cmd 之类的脚本文件。 创建一个文本文件,输入内容,然后另存为/重命名即可。

打开浏览器, 输入地址: http://localhost:8080

打开页面时, 会提示输入房间号。如果要加入同一个房间, 则两个客户端输入相同的房间号即可, 如 “cnc”。

打开一个新标签页, 输入地址: http://localhost:8080。 输入同样的房间号 cnc

再打开第三个标签页, 输入地址: http://localhost:8080。 也输入同样的房间号 cnc

然后查看每个选项卡对应的控制台日志信息, 应该可以看到JavaScript中打印的日志信息。

练习与实践

  1. 可以选择哪些消息传递机制? 如果使用纯粹的 WebSocket, 会遇到哪些问题?
  2. 扩展这个应用, 会涉及哪些问题? 你能用某种技术来模拟成千上万个并发请求吗?
  3. 在这个应用中, 使用了一个JavaScript prompt 来让用户输入房间号。 试着修改程序, 将房间号放到URL之中。例如 http://localhost:8080/cnc, 则对应房间号为 cnc

知识点回顾

在本节课程中, 我们学习了:

  • 通过 npm 安装 package.json 文件中指定的项目依赖
  • 运行Node.js服务器, 通过 node-static 提供静态文件服务。
  • 用Socket.IO创建消息传递服务
  • 创建聊天室以及发送聊天消息。

本节的完整版代码位于 step-04 文件夹中。

了解更多

后续内容

接下来, 我们将学习如何通过信令服务, 让两个客户端建立对等连接。

原文链接: https://codelabs.developers.google.com/codelabs/webrtc-web/#6

翻译人员: 铁锚 - https://blog.csdn.net/renfufei

翻译日期: 2018年08月27日

WebRTC基础实践 系列文章目录如下:

没有更多推荐了,返回首页