用 PeerJS 驯服 WebRTC:制作一个简单的 P2P 网页游戏

已发表: 2022-03-11

WebRTC 是一种实现 Web 浏览器之间实时通信的技术。 比较新,API定义还算草稿。 再加上并非所有主要网络浏览器都支持 WebRTC(在支持的浏览器中,有些浏览器并不支持该技术的所有功能),这使得将 WebRTC 用于任何关键任务应用程序变得相对困难. 或者你会这么想!

使用 PeerJS 通过 WebRTC 连接四个:看,没有服务器!

使用 PeerJS 通过 WebRTC 连接四个:看,没有服务器!
鸣叫

自 2011 年 5 月 Google 首次推出以来,WebRTC 已被用于许多现代 Web 应用程序中。 作为许多现代 Web 浏览器的核心功能,Web 应用程序可以无缝地利用该技术以多种方式提供改进的用户体验。 视频流或会议应用程序不需要臃肿的浏览器插件,并且可以利用对等 (P2P) 网络(而不是通过某些服务器传输所有数据)只是所有令人惊奇的事情的一部分可以通过 WebRTC 实现。

在本文中,我们将了解如何使用 WebRTC 制作连接四的简单 P2P 网页游戏。 为了解决 WebRTC 的各种粗糙边缘和实现差异,我们将使用一个惊人的 JavaScript 库:PeerJS。

WebRTC 上的数据

在我们开始之前,重要的是要了解 WebRTC 不仅仅是传输音频和视频流。 它还提供对 P2P 数据通道的支持。 这些渠道有两种变体:可靠和不可靠。 正如人们可能猜到的那样,可靠的数据通道保证消息被传递并按顺序传递,而不可靠的通道不提供这样的保证。

WebRTC 基础设施 - 缩写词的海洋

WebRTC 基础设施 - 缩写词的海洋
鸣叫

此外,WebRTC 数据通道不需要特殊的基础设施设置,除了典型的 WebRTC 对等连接所需的设置:用于协调对等之间连接的信令服务器、用于确定对等的公共身份的 STUN 服务器,以及可选的 TURN 服务器如果无法建立对等点之间的直接连接(例如,当两个对等点都在 NAT 之后),则在对等点之间路由消息。 如果这些首字母缩略词听起来很熟悉,那是因为 WebRTC 尽可能地重新利用了现有技术。

这为 WebRTC 的更多用例打开了大门,包括但不限于多人游戏、内容交付和文件共享。 同样,所有这些都不需要任何中间服务器,因此延迟更低。

在我们的简单网页游戏中,我们将使用两个网页浏览器之间的数据通道来来回交流玩家移动。

认识 PeerJS

PeerJS 在您的浏览器中实现 WebRTC,并围绕它包装了一个简单、一致且优雅的 API。 它填补了早期浏览器的 WebRTC 实现中的各种漏洞。 例如,在 Chrome 30 或更早版本中,只有不可靠的数据通道可用。 PeerJS,如果配置为使用可靠的数据通道,将为那些旧浏览器使用 shim。 尽管这不会像可靠通道的本机实现那样高效,但它仍然可以工作。

使用 PeerJS,识别对等点更加简单。 每个对等点都只使用一个 ID 来标识。 对等方可以选择自己的字符串,或者让服务器生成一个字符串。 尽管 WebRTC 承诺点对点通信,但您仍然需要一个服务器来充当连接代理并处理信号。 PeerJS 提供了这个连接代理服务器 PeerJS Server(用 Node.js 编写)的开源实现,以防您不想使用他们的云托管版本(现在是免费的,但有一些限制)。

连接四去 P2P

现在我们有了使用 WebRTC 的信心来源,即 PeerJS,让我们从创建一个简单的 Node.js/Express 应用程序开始。

 npm init npm install express --save npm install jade --save npm install peer --save

我们将仅使用它来托管 PeerJS 服务器,并提供页面和前端资产。 我们只需要提供一个页面,这将包含两个部分:一个普通的主菜单和一个 7×6 连接四个网格。

PeerJS 服务器

托管我们自己的 PeerJS 服务器非常简单。 GitHub 上的官方仓库甚至有一个一键按钮,可以将 PeerJS Server 的实例部署到 Heroku。

在我们的例子中,我们只想在我们的 Node.js 应用程序中创建一个ExpressPeerServer实例,并在“/peerjs”处提供它:

 var express = require('express') var app = express() // … Configure Express, and register necessary route handlers srv = app.listen(process.env.PORT) app.use('/peerjs', require('peer').ExpressPeerServer(srv, { debug: true }))

PeerJS 客户端

随着 PeerJS 服务器的启动和运行,我们进入客户端。 如前所述,PeerJS 使用唯一 ID 标识对等点。 这些 ID 可以由 PeerServer 自动为每个 peer 生成,或者我们可以在实例化Peer对象时为每个 peer 选择一个。

 var peer = new Peer(id, options)

在这里,如果我们希望服务器为我们生成一个,则可以完全省略id 。 在我们的例子中,这就是我们想要做的。 PeerServer 将确保它给出的 ID 是唯一的。 第二个参数options通常是一个包含密钥的对象(API 密钥,如果您使用云托管的 PeerServer,或者如果您自己托管 PeerServer,则为hostportpath等)。

 var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })

为了在两个 PeerJS 对等点之间建立连接,其中一个对等点必须知道另一个对等点的 ID。 为了简单起见,在我们通过 WebRTC 实现 Connect Four 的过程中,我们将要求开始游戏的玩家与他的对手共享他的对等 ID。 在已知目标对等 ID 的情况下,只需简单调用peer.connect(destId) 即可

 var conn = peer.connect(destId)

peer.connect(destId)返回的Peer对象和DataConnection对象都会发出一些非常有用的事件,值得倾听。 出于本教程的目的,我们对DataConnection对象的“数据”事件和两个对象的“错误”事件特别感兴趣。

为了向连接的另一端发送数据,只需调用conn.send(data)

 conn.send('hello')

虽然在这里我们的需求有点过头了,但 PeerJS 在将数据编码为 BinaryPack 格式之后在对等点之间传输数据。 这允许对等点通信字符串、数字、数组、对象甚至 blob。

要接收传入数据,只需在conn上侦听 'data' 事件:

 conn.on('data', function(data) { // data === 'hello' })

这几乎就是我们所需要的!

游戏逻辑

第一个开始游戏的玩家会看到由 PeerJS 生成的同伴 ID,他们可以与对手共享。 一旦对手使用第一个玩家的对等 ID 加入游戏,则允许第一个玩家移动。

连接四,作为一个简单的规则和机制的游戏,只有一种类型的移动:每个玩家依次必须选择一列并将一个圆盘放入其中。 这意味着对等点需要通信的只是当前玩家选择将他的光盘放入其中的列号。我们将将此信息作为包含两个元素的数组传输:字符串“移动”和数字 - 0-基于左侧列的索引。

每次玩家点击列时:

 if(!turn) { // it is not the current player's turn return } var i // i = chosen column index if(grid[i].length == 6) { // the column doesn't have any more space available return } // track player's move locally grid[i].push(peerId) // end current player's turn turn = false conn.send(['move', i])

将此移动数据发送给对手后,我们在本地更新游戏状态。 这包括确定当前玩家是否赢了,或者游戏是否以平局结束。

在此移动数据的接收端:

 if(turn) { // ignore incoming move data when it is the current player's turn return } var i = data[1] if(grid[i].length == 6) { // ignore incoming move data when it is invalid return } // track opponent's move locally grid[i].push(opponent.peerId) // activate current player's turn turn = true

当然,在这之后我们会在本地更新游戏状态,确定对手是否获胜或游戏是否以平局结束。

请注意我们需要如何对传入数据执行完整性检查。 这一点很重要,因为对于基于 WebRTC 的游戏,我们没有中间服务器和基于服务器的游戏逻辑来验证移动数据。

为了使代码片段保持简单,更新 UI 的代码行已被省略。 您可以在此处找到客户端 JavaScript 的完整源代码。

连接一切

为了将这一切结合起来,我们创建了一个包含两个部分的简单页面。 在页面加载时,显示包含主菜单的部分,包含游戏网格的部分保持隐藏。

 section#menu div.animated.bounceIn div h1 Connect Four br div.no-support() div.alert.alert-warning p Unfortunately, your web browser does not <a href="http://iswebrtcreadyyet.com">support WebRTC</a> div a.btn.btn-primary.btn-lg(href='#start') Start | &nbsp; a.btn.btn-default.btn-lg(href='#join') Join section#game() div div h1 Connect Four br table.table.grid tbody for i in [0, 1, 2, 3, 4, 5] tr for j in [0, 1, 2, 3, 4, 5, 6] td div.slot br div.alert.alert-info p

使这些 DOM 元素看起来很漂亮超出了本教程的范围。 因此,我们将求助于我们值得信赖的伙伴 Bootstrap 并对其进行一些简单的造型。

当第一个玩家点击“开始”按钮时,游戏网格与玩家的同伴 ID 一起显示。 然后玩家可以与他们的对手共享这个对等 ID。

开始一个新游戏,并分享您由 PeerJS 生成的对等 ID

第二个玩家可以点击然后点击“加入”按钮,输入第一个玩家的peer ID,然后开始游戏。

使用对手的同伴 ID 加入游戏

试一试

您可以在 https://arteegee.herokuapp.com 试用此示例应用程序。

或者,您可以从 GitHub 克隆存储库,安装 NPM 依赖项,然后在本地试用:

 git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm start

服务器运行后,您可以将 Web 浏览器指向 http://localhost:5000,从一个选项卡开始游戏,然后使用对等 ID 从另一个选项卡(甚至是支持 WebRTC 的不同 Web 浏览器)加入。

您可以打开 Web 浏览器的控制台以查看一些调试信息,如在此示例应用程序中,PeerJS 客户端已配置为执行详细日志记录。

但这对我不起作用!

此游戏可能无法在您的计算机上运行的主要原因有两个。

您使用的 Web 浏览器可能还不支持必要的 WebRTC API。 如果是这种情况,您可能需要尝试其他浏览器 - 支持 WebRTC 和数据通道的浏览器。

如果您使用支持 WebRTC 的现代 Web 浏览器,那么您有可能落后于 WebRTC 无法渗透的某些网络基础设施。 理想情况下,这个问题可以通过 TURN 服务器轻松解决,但由于示例应用程序没有使用一个,因此当您和您的对手都在 NAT 之后时,它将无法工作。

结论

WebRTC 是一项新技术,其实现还远未成熟。 这些通常会给开发人员带来一些独特的挑战。 然而,随着像 PeerJS 这样巧妙地抽象出粗糙的原始 API 的库的可用性,该技术已经变得非常容易使用。

我希望这个构建基于 PeerJS 的游戏的简短教程能够帮助您开始使用 WebRTC 并构建一些令人惊叹的实时点对点 Web 应用程序。