7.设置信令服务来交换消息

你会学到什么

在此步骤中,您将了解如何:

  • 使用npm来安装package.json中指定的项目依赖关系

  • 运行Node服务器并使用node-static来提供静态文件。

  • 使用socket.io在Node上设置消息传递服务。

  • 使用它来创建“房间”和交换消息。

此步骤的完整版本在步骤-04文件夹中。

概念

为了建立和维护WebRTC呼叫,WebRTC客户端(对等体)需要交换元数据:

  • 候选(网络)信息。

  • 提供和回答消息提供媒体信息,如分辨率和编解码器。

换句话说,在可以进行音频,视频或数据的点对点流传输之前需要进行元数据的交换。这个过程称为信令(signaling)。

在前面的步骤中,发送方和接收方RTCPeerConnection对象位于同一页面上,因此“信令”仅仅是在对象之间传递元数据的问题。

在现实世界的应用中,发送者和接收者RTCPeerConnections在不同设备上的网页中运行,我们需要一种方式来传达元数据。

为此,我们使用信令服务器:可以在WebRTC客户端(对等体)之间传递消息的服务器。实际的消息是纯文本:字符串化的JavaScript对象。

关于应用程序

在这一步中,我们将构建一个简单的Node.js信令服务器,使用Socket.IO Node模块和JavaScript库进行消息传递。 Node.js和Socket.IO的经验将是有用的,但并不重要;消息传递组件非常简单。

选择正确的信令服务器

该codelab使用Socket.IO作为信令服务器。

Socket.io的设计使得它能够直接构建一种交换消息的服务,而Socket.io由于其内置的“房间”概念,适合于WebRTC信令。

对于生产级别的服务,可能会有更好的选择。请参阅如何为您的下一个WebRTC项目选择信令协议

在此示例中,服务器(Node应用程序)在index.js中实现,运行在其上的客户机(Web应用程序)在index.html中实现。

此步骤中的Node应用程序有两个任务。

首先,它作为一个消息传递:

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

其二,它管理WebRTC视频聊天室:

if (numClients === 1) {
  socket.join(room);
  socket.emit('created', room, socket.id);
} else if (numClients === 2) {
  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或Command-Option-J(如果您在Mac上)。

用以下代码替换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在Node上运行

对于以下步骤,您将在Node上运行Socket.IO。

在工作目录的顶层创建一个名为package.json的文件,其中包含以下内容:

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

要安装依赖项,请从工作目录中的命令行终端运行以下命令:

npm install

在工作目录的顶层创建一个新的文件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 numClients = io.sockets.sockets.length;
    log('Room ' + room + ' now has ' + numClients + ' client(s)');

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

    } else if (numClients === 2) {
      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);
        }
      });
    }
  });

});

从命令行终端,在工作目录中运行以下命令:

node index.js

从您的浏览器中,打开localhost:8080。

每次打开此URL时,系统将提示您输入房间名称。要加入同一个房间,每次选择相同的房间名称,如“foo”。

打开一个新的标签页,再打开localhost:8080。选择相同的房间名称。

在第三个选项卡或窗口中打开localhost:8080。再次选择相同的房间名称。 检查每个选项卡中的控制台:您应该看到上述JavaScript中的日志记录。

加分点

  1. 可能有什么替代的消息传递机制?使用“纯”WebSocket可能会遇到什么问题?

    旧浏览器可能不支持websocket,需要降级为轮询。socket.io解决了向下兼容旧浏览器的问题。
  2. 扩展此应用程序可能涉及哪些问题?你可以开发一种方法来测试成千上万的房间同步请求吗?

    压力测试。在npm寻找压力测试包,书写测试代码
  3. 此应用程序使用JavaScript prompt获取房间名称。找出一种从URL获取房间名称的方法。例如localhost:8080/foo会给房间名称foo。

    处理URl参数,找出path参数,例如/foo

你学到了什么

在这一步,你学会了如何:

  • 使用npm来安装package.json中指定的项目依赖关系

  • 运行Node服务器来服务静态文件。

  • 使用socket.io在Node上设置消息传递服务。

  • 使用它来创建“房间”和交换消息。

此步骤的完整版本在步骤-04文件夹中。

学习更多

接下来

了解如何使用信令使两个用户能够进行对等连接。

Last updated