SSE(Server-Sent Events 服务器推送事件)
正在加载今日诗词....
2024-04-28

SSE(Server-Sent Events 服务器推送事件),是一种特殊的 HTTP 请求,在浏览器建立连接后,服务器可以分批次、有间隔的向浏览器下发数据,它常常拿来与 websocket 做比较

SSEwebsocket
单向连接双向连接
HTTP 协议TCP 协议
内置断线重连-
UTF-8 文本数据流消息类型广泛

概念

浏览器使用EventSource建立连接

const evtSource = new EventSource('//xxx/xx', { withCredentials: true })

服务器使用 text/event-stream MIME 类型响应内容

res.writeHead(200, {
  'Content-Type': 'text/event-stream',
  Connection: 'keep-alive',
  'Cache-Control': 'no-cache',
  'Access-Control-Allow-Origin': '*', // 允许跨域
})

响应内容是一个 UTF-8 的文本数据流,由:分隔字段名与值,由 \n\n 分隔消息。 以:开头的行为注释行,会被忽略。 合法字段名只有:event 事件名、data 数据、id、retry 毫秒。

res.write(`event: testEvent\n`)
res.write(`id: 123\n`)
res.write(`retry: 30000\n`)
res.write(`data: {"a":4}\n\n`)

浏览器使用 onmessage 和 addEventListener 监听服务器消息,没有指定 event 会触发前者,否则触发后者

evtSource.onmessage = function (event) {
  console.log('收到无 event 字段消息', event)
}
evtSource.addEventListener('testEvent', (event) => {
  console.log('收到指定 event 事件', event)
})

连接应由浏览器关闭,因为 EventSource 自带断线重连

evtSource.onerror = (err) => {
  console.error('EventSource failed:', err)
}
evtSource.close()
req.on('close', () => {
  res.end()
})

demo

官方推荐使用EventSource类创建 sse 连接,但它只能建立 get 请求,且无法自定义 header

npm 上现在已经有不少相关包了,基于 Fetch、XMLHttpRequest 模拟了 EventSource 的 API,并支持了自定义 header 等功能,比如sse.js

// NODE
const http = require('http')
http
  .createServer((req, res) => {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Access-Control-Allow-Origin': '*', // 允许跨域
    })
    // 连接由浏览器关闭
    req.on('close', () => {
      res.end()
    })
    setTimeout(() => sendTestEvent(res), 1000)
    setTimeout(() => sendSimpleEvent(res), 2000)
  })
  .listen(3000)
function sendTestEvent(res) {
  res.write(`event: testEvent\n`)
  res.write(`id: 123\n`)
  res.write(`retry: 30000\n`)
  res.write(`data: {"a":4}\n\n`)
}
function sendSimpleEvent(res) {
  res.write(`data: this is SimpleEvent\n\n`)
}
<script
  type="module"
  src="https://cdn.jsdelivr.net/npm/sse.js@2.4.1/lib/sse.js"
></script>
<script>
  var evtSource = new SSE('http://localhost:3000')
  evtSource.addEventListener('testEvent', (event) => {
    console.log('收到指定 event 事件', event.data)
    // => 收到指定 event 事件 {"a":4}
  })
  evtSource.onmessage = function (event) {
    console.log('收到无 event 字段消息', event.data)
    // => 收到无 event 字段消息 this is SimpleEvent
    evtSource.close()
  }
</script>

缺点/需要注意的点

  • 因为 HTTP1.1 并发连接数是 6 个,打开多个标签页时会很麻烦
  • 兼容性:ie 不支持,edge 20 年开始支持,其它都支持的不错

京ICP备2022027737号
Copyright © 2022 - present @wangxiang

  • ☀️
  • 🌑