使用 WebRTC 进行视频通话第 2 步:通过 WebSocket 建立连接

照片来自Unsplash上的Cytonn Photography

以下是本系列文章的链接:

1.来自网络摄像头和麦克风的数据流(访问用户设备以及数据流,并在浏览器上显示视频(带音频))
2.通过 WebSocket 建立连接(在两个用户之间通过 WebSocket 建立 P2P 连接)
3.建立 WebRTC 连接( 建立 WebRTC 连接,真正开启视频聊天)
4.查找联系人(调整 WebSocket 服务器和客户端代码,使用户只有输入相同代码才会建立彼此联系)
5.与 WebRTC 共享您的屏幕(将 RTCPeerConnection 对象的视频轨道替换为显示媒体流的视频轨道,使用户之间共享屏幕)

我们在上一篇文章中 知道了如何从浏览器访问网络摄像头和麦克风流,现在要开始在两个用户之间建立 P2P 连接。

本地浏览器和远程用户(对等方)之间的 WebRTC 连接将由 JavaScript 接口RTCPeerConnection 表示。但你必须先协调这个连接,在对等点之间交换消息以找到彼此位置,控制通信然后终止它,这就是信令过程。信令过程不是 WebRTC 规范的一部分,你可以自由使用任何想要建立和控制连接的消息传递协议。从技术层面上讲,你可以在每个帖子中传递这些消息,但常见的解决方案是使用 WebSocket 协议。

在本文中,我们将允许两个用户通过 WebSocket 进行通信。我们将使用 Node 服务器作示范,其中对等方可以创建连接、互相打招呼并关闭连接。

WebSocket

WebSocket 是一种通信协议,类似于 HTTP。与 HTTP 一样,WebSocket 支持客户端(浏览器)和服务器之间的通信。

当通过HTTP进行 通信时,服务器只能对客户端的请求做出反应。它不记得以前请求的内容,HTTP 是无状态的。另一方面,WebSocket 允许你在客户端和服务器之间创建一个通道。一旦这个通道打开,直到它关闭,通信都可以双向进行:服务器可以随时发送数据,客户端也可以。

WebSocket 实际上的设计宗旨是通过为HTTP 工作,因此我们可以使用普通的 Web 服务器来创建它。我们将在客户端和 Node 服务器之间实现 WebSocket 通信。

设置Node项目

为此,我们将使用 Node 服务器和一个能实现 WebSocket 的库:WebSocket-Node

如果你还没有安装,你应该先安装Node.js。创建要在其中放置 WebSocket 项目的文件夹(我将其命名为web-socket )。然后进入该文件夹初始化你的项目:

npm init

Create a file index.js and install the WebSocket-Node library using npm:

创建文件 index.js 并使用 npm安装WebSocket-Node库:

npm install websocket --save

按照库的文档,我们首先需要定义一个 HTTP 服务器。我们将侦听端口 1337。然后我们在它的顶部创建 WebSocket 服务器。

const http = require('http');
const server = require('websocket').server;

const httpServer = http.createServer(() => { });
httpServer.listen(1337, () => {
  console.log('Server listening at port 1337');
});

const wsServer = new server({
  httpServer,
});

我们想进行一些简单操作来理解 WebSocket 的工作原理。每个人都可以通过我们的服务器连接和发送消息。当用户发送消息时,所有其他与之连接的用户都会收到它。

let clients = [];

wsServer.on('request', request => {
  const connection = request.accept();
  const id = Math.floor(Math.random() * 100);

  clients.forEach(client => client.connection.send(JSON.stringify({
    client: id,
    text: 'I am now connected',
  })));

  clients.push({ connection, id });

  connection.on('message', message => {
    clients
      .filter(client => client.id !== id)
      .forEach(client => client.connection.send(JSON.stringify({
        client: id,
        text: message.utf8Data,
      })));
  });

  connection.on('close', () => {
    clients = clients.filter(client => client.id !== id);
    clients.forEach(client => client.connection.send(JSON.stringify({
      client: id,
      text: 'I disconnected',
    })));
  });
});

我们需要跟踪连接的用户,我们在客户端 数组中这样做。当用户请求连接时,我们先点击接受,然后通知所有其他已连接用户。接着,我们生成一个随机 id,然后将具有此 id 的新连接添加到数组中。

当用户发送消息时,会发出事件消息 。除了发送消息的用户之外,每个连接的用户都会收到一些数据,其中包含消息的文本和发送消息的用户的索引。

最后,当用户断开连接时,他会被移出客户端 列表,并且其余用户都会收到有关它的通知。

现在让我们通过创建一个简单的用户界面来检查这是否有效。

客户端

我们不会在客户端中创建任何复杂的东西,我们只想确保通信正常运行。我们将显示三个按钮:

  • 一键连接
  • 一键发送“你好!” 信息
  • 一键断开

当用户未连接时,仅启用第一个按钮,否则仅启用最后两个按钮。

在按钮下方,我们显示了一些将要打印消息的区域(在代码中称为控制台 )。

你可以为客户端代码创建一个新文件夹(我将其命名为我的客户端 )。再操作一遍,我们可以让事情变得更简单,你只需要index.htmlindex.jsstyles.css 文件。HTML 和 CSS 就非常简单:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>VideoChat</title>
  <script src="index.js"></script>
  <link rel="stylesheet" href="styles.css">
</head>

<body>
  <button id="start">Connect to WebSocket</button>
  <button id="say-hello" disabled>Say Hello</button>
  <button id="close" disabled>Leave</button>
  <div id="console"></div>
</body>

</html>
button {
  display: block;
  margin: auto;
  margin-bottom: 10px;
  padding: 10px;
  font-size: 16px;
  background-color: seagreen;
  border: 1px solid seagreen;
  color: white;
  border-radius: 3px;
  font-weight: bold;
  cursor: pointer;
}

button:disabled {
  cursor: not-allowed;
  background-color: grey;
  border: 1px solid grey;
}

#console {
  width: 400px;
  height: 400px;
  border: 1px solid black;
  border-radius: 3px;
  margin: auto;
  margin-top: 30px;
}

你可以在下面找到整个index.js 文件,但为了使其更易于理解,我们首先将重点放在摘录上。

我们需要定义点击按钮时会发生的反应。单击第一个按钮时,我们要初始化连接。当第二个按钮被点击时,我们想通过连接发送消息“你好!” 。单击最后一个按钮应关闭连接。connection 是一个全局变量,在setupWebSocketConnection 函数中被初始化。

  document.addEventListener('click', async event => {
    if (event.target.id === 'start') {
      setupWebSocketConnection();
    } else if (event.target.id === 'say-hello') {
      connection.send('Hello!');
    } else if (event.target.id === 'close') {
      closeConnection();
    }
  });

最有趣的部分当然是如何建立连接。

  const setupWebSocketConnection = () => {
    connection = new WebSocket('ws://127.0.0.1:1337');

    connection.onopen = () => {
      addMessageToConsole('You are now connected!');
      enableAndDisableButtons(true);
    };

    connection.onerror = error => {
      console.log(`An error occured: ${error}`)
    };

    connection.onmessage = message => {
      const data = JSON.parse(message.data);
      addMessageToConsole(`Client${data.client} says: ${data.text}`)
    };
  }
  

我们只需创建一个WebSocket 类的实例,将 url 作为参数传递。然后我们定义事件处理程序。当连接打开时,我们向控制台添加一条消息并正确设置按钮的禁用属性。当收到消息时,我们将其显示在控制台中。

连接打开后,通过它发送消息就简单多了:

connection.send('Hello!');

以上你可以在事件侦听器函数中看到。关闭连接也很简单,正如在closeConnection 函数中看到的:

  const closeConnection = () => {
    connection.close();
    addMessageToConsole('You disconnected!');
    enableAndDisableButtons(false);
  }
  

和所说的一样,这里是完整的index.js 文件:

(function () {
  "use strict";

  let connection;

  const enableAndDisableButtons = (connected) => {
    document.getElementById('start').disabled = connected;
    document.getElementById('say-hello').disabled = !connected;
    document.getElementById('close').disabled = !connected;
  }

  const setupWebSocketConnection = () => {
    connection = new WebSocket('ws://127.0.0.1:1337');

    connection.onopen = () => {
      addMessageToConsole('You are now connected!');
      enableAndDisableButtons(true);
    };

    connection.onerror = error => {
      console.log(`An error occured: ${error}`)
    };

    connection.onmessage = message => {
      const data = JSON.parse(message.data);
      addMessageToConsole(`Client${data.client} says: ${data.text}`)
    };
  }

  const closeConnection = () => {
    connection.close();
    addMessageToConsole('You disconnected!');
    enableAndDisableButtons(false);
  }

  const addMessageToConsole = message => {
    const messageDiv = document.createElement('div');
    messageDiv.textContent = message;
    document.getElementById('console').appendChild(messageDiv);
  }


  document.addEventListener('click', async event => {
    if (event.target.id === 'start') {
      setupWebSocketConnection();
    } else if (event.target.id === 'say-hello') {
      connection.send('Hello!');
    } else if (event.target.id === 'close') {
      closeConnection();
    }
  });
})();

测试

现在启动服务器并在两个客户端之间尝试我们的消息传递系统。要运行服务器,需转到web-socket 文件夹并运行:

node index.js

你应该在终端中看到“服务器在端口 1337 上侦听”。

要测试两个客户端是否可以通过服务器进行通信,需在两个选项卡或两个浏览器窗口中打开index.html

现在随意单击按钮,你可以看到客户端之间正在交换消息:

通过 WebSocket 的通信正在工作。

我们设置了一个 WebSocket 服务器以允许两个客户端交换消息。换个思路,我们现在可以使用这个通道来交换必要的消息创建和控制 WebRTC 连接。这就是我们在下一篇文章中要做的内容。

原文作者 Heloise Parein
原文链接 https://levelup.gitconnected.com/set-up-a-connection-over-websocket-videochat-with-javascript-step-2-f78c307c4fd3

推荐阅读
相关专栏
开源技术
106 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。