4

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

 3 years ago
source link: https://renfufei.blog.csdn.net/article/details/84586900
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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基础实践 系列文章目录如下:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK