Skip to content

为了能使 SSE 正常工作,Server 端也需要作一些对应设置。

本节中 server 端相关代码,以 express 框架作为示范。

js
app.get('/api/sse', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')
  res.setHeader('Connection', 'keep-alive')

  const startTime = Date.now()

  const timer = setInterval(() => {
    if (Date.now() - startTime >= 3000) {
      clearInterval(timer)
      // 测试发现 id字段不在data字段后 也起作用
      res.write('event:foo\nid:foo\nretry: 10000\ndata:{ message: \'foo\' }\n\n')
      const data = { message: 'close', timestamp: new Date() }
      res.write(`data:${JSON.stringify(data)}\n\n`)
      // res.end() 不会关闭SSE连接 但会触发error事件监听
      res.end()
      return
    }
    const data = { message: 'HelloWorld', timestamp: new Date() }
    res.write(`data:${JSON.stringify(data)}\n\n`)
  }, 1000)
})

1.响应头配置

SSE 的响应头配置是固定的,有以下三条:

js
// 定义响应数据为事件流类型
res.setHeader('Content-Type', 'text/event-stream')

// 定义客户端不进行清缓存 保证每次请求结果都是更新的
res.setHeader('Cache-Control', 'no-cache')

// 定义长连接 客户端EventSource会发送Connection请求头 此处作对应设置以保证长连接
res.setHeader('Connection', 'keep-alive')

TIP

测试发现,Connection 设置为 keep-alive,该项非必需。

因为目前大部分后端程序会设置该项为默认值:

  • Connection: keep-alive
  • keep-alive: timeout=5

2.响应体格式

SSE一次的响应体推送,我们称作一个 message

message 的数据格式是字符串文本

message[field]: value 键值对格式的字符串拼接起来:

  • 每个键值对之间需要使用 \n 分割开来
  • 每个 message 末尾以 \n\n 结尾

field 只支持如下 4 个固定字段:

  1. event
  2. data
  3. id
  4. retry

单个 message 的格式可能如下:

js
// 1
`event:foo\nid:foo\nretry: 10000\ndata:{ message: 'foo' }\n\n`

// 2
`event:bar\ndata:{ message: 'bar' }\n\n`

2-1.event

event 字段用以自定义事件。

客户端可使用 addEventListener 作对应监听。

如果没有声明 event 字段,则默认触发 message 事件。

js
// server
res.write(`event:foo\nid:foo\nretry: 10000\ndata:{ message: 'foo' }\n\n`)

// client
source.addEventListener('foo', (event) => {
  console.log(event.data)
})

2-2.data

data 字段用以设置事件数据。

客户端可使用 event.data 属性进行接收。

2-3.id

id 字段用以标识会话 ID。主要用以 SSE 重连。

当设置了 id 字段后,如果 SSE 网络连接中断,那么在恢复重连后,会自动在请求头中设置 Last-Event-Id 字段。

Last-Event-Id 字段值与 id 字段值相对应。

TIP

恢复重连,是客户端自动进行的

如果没有显示的使用 source.close() 方法进行关闭 SSE 连接,则每一次中断过后,经过一段时间,客户端都会自动重连。

2-4.retry

在上一节 id 字段的相关介绍中,我们提到了中断一定时间后,客户端都会自动重连。

retry 字段的功能就是用以设置中断时间具体时长。

retry 是有默认值的,笔者没有找到文档,根据实际代码推测是 3000ms 左右。

可以用如下代码设置覆盖:

js
res.write(`retry: 10000\ndata:{ message: 'Hello' }\n\n`)