使用 Server-sent Events 和 Node.js 实现实时网络游戏

· 约 12 分钟读完

在现代互联网应用中,实时通信技术变得越来越重要,这在网络游戏中尤其突出。在传统的网络游戏中,玩家需要手动在游戏客户端中进行信息的发送和接收。这种方式虽然可以保证高效的实时性,但是很难处理并发的数据连接,特别是在高网络负载的情况下。

Server-sent Events (SSE) 是一种非常适合实现实时信息交流的协议,它可以让服务器向客户端推送数据,SSE 和 WebSocket 很像,但是 SSE 更加稳定和易用,并且不需要复杂的协议和额外的库来支持。与 WebSocket 不同,SSE 是基于 HTTP 的单向数据流,更适合常规的 HTTP 应用程序和服务。

在本文中,我们将使用 SSE 和 Node.js 来构建一个简单的实时网络游戏,展示其优雅的设计和强大的性能。

开始构建

我们需要用到 Node.js 的一些核心模块(http、fs、url、path),首先创建一个新的目录并在其中创建一个新的 package.json 文件:

接下来,我们安装 express 和 socket.io 这两个依赖:

然后创建一个 index.js 文件,将以下内容放入其中:

这里使用了 express 来处理静态资源并提供一个简单的页面。

我们还需要创建一个 client 文件夹,并在其中创建一个 index.html 文件,以下是示例代码:

在这里,我们通过 socket.io.js 资源引入了 socket.io,这个库将在稍后用于建立实时连接。我们还在这个页面中添加了一个 canvas 元素,稍后将用来渲染游戏画面。

构建客户端代码

为了完成我们的客户端代码,我们首先需要在 client 目录中创建一个名为 client.js 的文件,以下是示例代码:

在这里,我们首先实例化了一个 SSE 对象(使用了 EventSource 的构造函数),并指定服务器的 /sse 连接作为 URL。SSE 对象可以收到服务器端推送的信息。

接下来,我们分别处理连接和信息推送。当连接失败时,我们向控制台输出一个信息。当收到信息时,我们向控制台输出该信息。

向 SSE 发送信息

现在,我们更新服务器端代码,以便向客户端发送 SSE 消息。要完成此操作,我们必须在 http 响应中包含与 SSE 相关的首部,例如 'Content-Type': 'text/event-stream'。

为了实现 SSE 的推送,我们需要三个步骤:1)打开一个 solo(即每个连接独立的)的通道;2)对每个通道发送一个头部信息;3)对每个通道发送一个内容消息。

以下是我们更新后的 index.js 的代码,其中包括上述三个步骤:

在此代码中,我们为 SSE 连接创建了一个新的路由,并使用 writeHead 方法设置了此连接的响应头。我们使用了 SSE 的格式来建立持久连接,这个连接将不会被立即关闭,同时每个事件通过回车和换行符来标记(\n)。在推送的消息中必须包含 data: 实体。

在发送的第一条消息中,我们将 retry 设置为 10000(即 10 秒),这意味着如果服务器端无响应,客户端将在 10 秒后尝试重新连接。如果您的服务器非常稳定,您也可以将其设置为 0。

当用户接入时,我们将发送一个“连接成功”的消息(设置 res.write),并为 socket 建立一个断开连接的事件处理程序。在每个连接断开时,我们将向连接的 SSE 通道发送一个消息。

游戏业务逻辑

现在,我们已经建立了 SSE 通道并且可以向客户端发送实时消息,但是我们需要一个更有意思的游戏(而不仅仅是连接和断开连接)才能展示这种技术的真正威力。因此,在这一节中,我们将为我们的游戏添加一些业务逻辑。

为了达到我们的目的,我们将需要两个主要的客户端脚本:一个是渲染我们的游戏,另一个是控制我们的游戏。

以下是 client/game.js(即第一个脚本):

在这里,我们定义了一个 Game 类,该类存储了游戏中的所有状态信息,因此我们可以更轻松地进行游戏逻辑处理。在构造函数中,我们设置了一个 obstacles 数组,用于在随机位置添加障碍物,以及一个 players 对象,将存储每个玩家的位置和状态。

该类中的 update 方法用于更新玩家的位置,并检测是否与其他物体产生碰撞。最后,我们的 collision 方法会检查当前正面临的所有障碍物,并检查所有玩家的位置是否与障碍物重叠。如果重叠,我们将重新生成该位置。这将确保游戏中的玩家和物体不会产生重叠。

以下是 client/client.js(第二个脚本):

在此 client.js 脚本中,我们首先获取了 canvas,设置了 SSE 连接,并创建了一个 renderPlayers 函数,用于在 canvas 中渲染每个玩家。

在处理 SSE 的信息推送时,我们分别检查了三种不同类型的消息:'connect'(即连接成功),'update'(即状态更新)和'disconnect'(即离线通知)。在每种情况下,我们都采取不同的行动:对于连接,我们从响应数据中提取玩家 ID;对于更新,我们解析返回的 JSON 数据,并使用 renderPlayers 函数更新画布。对于断开连接,我们只是记录信息。

最后,我们还添加了一个键盘监听器,用于处理用户按下上、下、左、右键后发送 SSE 消息。

运行游戏

我们已经准备好了所有服务器和客户端的代码,现在需要编译并运行它。运行以下命令开始:

接下来,打开浏览器,并访问 http://localhost:3000,您应该看到一个具有游戏画布的页面。打开 DevTools(按 F12),并切换到 Console 标签页。然后,打开一个新标签页,并使用相同的 URL 访问两次。

现在,我们已经成功地在本地运行了我们的基于 SSE 的网络游戏!您应该可以看到两个黑色的小球,在随机位置移动和重生,如果它们碰到障碍物,它们就会消失并在新地方重新生成。您还可以按上、下、左、右键来操纵小球。如果您在一个标签页上移动小球,那么第二个标签页上的小球也会发生相同的移动。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d7e732a941bf7134e21c53