本节内容
在本节课程中, 我们将学习以下内容:
- 通过
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-04 到 step-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中打印的日志信息。
练习与实践
- 可以选择哪些消息传递机制? 如果使用纯粹的 WebSocket, 会遇到哪些问题?
- 扩展这个应用, 会涉及哪些问题? 你能用某种技术来模拟成千上万个并发请求吗?
- 在这个应用中, 使用了一个JavaScript prompt 来让用户输入房间号。 试着修改程序, 将房间号放到URL之中。例如 http://localhost:8080/cnc, 则对应房间号为
cnc
。
知识点回顾
在本节课程中, 我们学习了:
- 通过
npm
安装package.json
文件中指定的项目依赖 - 运行Node.js服务器, 通过 node-static 提供静态文件服务。
- 用Socket.IO创建消息传递服务
- 创建聊天室以及发送聊天消息。
本节的完整版代码位于 step-04
文件夹中。
了解更多
- Socket.io chat-example repo
- WebRTC in the real world: STUN, TURN and signaling
- The term ‘signaling’ in WebRTC
后续内容
接下来, 我们将学习如何通过信令服务, 让两个客户端建立对等连接。
原文链接: https://codelabs.developers.google.com/codelabs/webrtc-web/#6
翻译人员: 铁锚 - https://blog.csdn.net/renfufei
翻译日期: 2018年08月27日
WebRTC基础实践 系列文章目录如下: