在 Node.js 中使用 Server-sent Events 遇到的问题及解决方法

阅读时长 5 分钟读完

前言

Server-sent Events(以下简称 SSE)是一种 HTML5 中新增的协议,主要用于服务器向客户端推送数据。与 WebSocket 相比,SSE 更加轻量级,并且可以使用浏览器内置的 EventSource 实现。在 Node.js 中,我们使用 http 模块来实现 SSE。

本文主要记录在使用 Node.js 实现 SSE 时遇到的一个棘手的问题及解决方法。

问题描述

在使用 SSE 推送数据时,我们可以监听 http 模块的 request 事件,建立一个 HTTP 连接,并通过响应流不断地向客户端推送数据。以下是一个 SSE 服务的示例代码:

-- -------------------- ---- -------
----- ---- - ----------------

----------------------- ---- -- -
  ------------------ -
    --------------- --------------------
    ---------------- -----------
    ------------- ------------
  ---

  -------------- -- -
    ----- ---- - --- ----------------------------
    ---------------- --------------
  -- ------
----------------

---------------- ------ -- --------- -- -------

以上代码会向客户端每秒钟推送一个包含当前时间的数据块。

客户端可以通过以下代码来监听 SSE 事件:

以上代码通过 EventSource 对象,监听 SSE 的 message 事件,并将接收到的数据输出到控制台。

按照上述代码运行,我们可以在控制台看到每秒钟输出一个时间字符串。

然而,这种做法有一个明显的缺点:如果在服务端关闭连接或发生错误时,客户端都无法获得提示或重新连接。为了解决这个问题,我们可以在服务端向客户端发送一个 ping 数据,用来保持连接。

于是,我们可以修改服务端代码,在每隔一段时间发送一个类似于 ping 的数据块,来保持连接:

-- -------------------- ---- -------
----- ---- - ----------------

----------------------- ---- -- -
  ------------------ -
    --------------- --------------------
    ---------------- -----------
    ------------- ------------
  ---

  -------------- -- -
    ----- ---- - --- ----------------------------
    ---------------- ------------
    ----------------- -----------
  -- ------
----------------

---------------- ------ -- --------- -- -------

这时,服务端会每秒钟向客户端发送两个数据块:一个是包含当前时间的数据块,另一个是一个 event 字段为 ping 的数据块,用来保持连接。

然而,这种做法也存在一个问题:在 Safari 浏览器下,当服务端发送一个 data 字段为空的数据块时,客户端会自动关闭连接,并抛出以下错误信息:

这使我们无法使用上述方法保持连接。接下来,我们将解决这个问题。

问题分析

通过搜索相关知识,我们可以了解到,在 Safari 中,如果一个 SSE 响应中包含一个空的数据块 data: \n\n,客户端会自动关闭连接。

与此相反,在其他浏览器中,响应中包含空的数据块并不会导致连接关闭。

为了解决这个问题,我们需要想办法避免在响应中出现空的数据块。

解决方案

为了避免出现空的数据块,我们可以在服务端向客户端发送一个不包含有效数据的数据块,来保持连接。这个数据块可以是一个空的 comment 字段(即 data: \n\n),或一个不包含 data 字段的数据块。

以下是修改后的示例代码:

-- -------------------- ---- -------
----- ---- - ----------------

----------------------- ---- -- -
  ------------------ -
    --------------- --------------------
    ---------------- -----------
    ------------- ------------
  ---

  -------------- -- -
    ----- ---- - --- ----------------------------
    ---------------- ------------
    ------------ -----------
  -- ------
----------------

---------------- ------ -- --------- -- -------

以上代码通过在 data 字段前插入一个冒号 :,使得响应中不包含空的数据块。

现在,我们就成功地避免了在 Safari 中出现自动关闭连接的问题。

总结

本文记录了在使用 Node.js 实现 SSE 服务时遇到的一个 Safari 浏览器中的 bug,并提供了相应的解决方案。在实际的开发过程中,我们要时刻关注各种浏览器的兼容性问题,并根据不同的环境做出相应的适配处理。

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

纠错
反馈