SSE (Server-Send Events) 服务端实时推送技术

在 HTTP 协议中,服务器无法向浏览器推送信息,可以使用 WebSocket 来实现两者双向通信。而SSE(Server-Sent Events),在浏览器向服务器请求后,服务器每隔一段时间向客户端发送流数据(是单向的),来实现接收服务器的数据,例如在线视频播放。

一、SSE 是什么

SSE(Server-Sent Events)是一种基于 HTTP 协议的推送技术。服务端可以使用 SSE 来向客户端推送数据,但客户端不能通过SSE向服务端发送数据。

二、应用场景

  1. 实时通知:在很多情况下,用户期望实时接收到应用的通知,如新消息提醒、商品活动提醒等。
  2. 节省资源:如果没有服务端推送,客户端需要通过轮询的方式来获取新信息,会造成客户端、服务端的资源损耗。通过服务端推送,客户端只需要在收到通知时做出响应,大大减少了资源的消耗。
  3. 增强用户体验:通过服务端推送,应用可以针对特定用户或用户群发送有针对性的内容,如优惠活动、个性化推荐等。
  4. chatGPT

三、与websocket 区别

SSE websocket
通信 单向通信 双向通信
协议 HTTP websocket
自动重连 支持 不支持,需要客户端自行支持
数据格式 文本格式,需要二进制数据需要二外编码 默认二进制数据、支持文本
浏览器支持 大部分主流现代浏览器 大部分主流现代浏览器
优势:
  • 简单易用:基于标准的HTTP协议,易于实现和调试。
  • 轻量级:不需要复杂的协议支持,如WebSocket。
  • 高效的网络利用:相比轮询,SSE减少了网络流量和延迟。
局限性
  • 单向通信:仅支持从服务器到客户端的数据流。
  • 浏览器支持:尽管大多数现代浏览器都支持SSE,但存在兼容性问题,尤其是在旧版浏览器中。
  • 缺乏控制:相比WebSocket,SSE提供的控制较少,例如无法控制连接的关闭。

四、数据格式

事件流是一个简单的文本流,仅支持 UTF-8 格式的编码。每条消息以一个空行作为分隔符。

在规范中为消息定义了 4 个字段:

event 消息的事件类型。客户端收到消息时,会在当前的 EventSource 对象上触发一个事件,这个事件的名称就是这个字段的值,如果消息没有这个字段,客户端的 EventSource 对象就会触发默认的 message 事件。

id 这条消息的 ID。客户端接收到消息后,会把这个 ID 作为内部属性 Last-Event-ID,在断开重连 成功后,会把 Last-Event-ID 发送给服务器。

data 消息的数据字段。客户端会把这个字段解析为字符串,如果一条消息有多个 data 字段,客户端会自动用换行符 连接成一个字符串。

retry 指定客户端重连的时间。只接受整数,单位是毫秒。如果这个值不是整数则会被自动忽略。

服务端

1.服务器首先向客户端声明接下来发送的是事件流( text/event-stream )类型的数据,然后就可以向客户端多次发送消息。

服务器端发送的数据的HTTP头信息如下:

css 复制代码
  res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
      'Access-Control-Allow-Origin': '*'
    })

数据格式

makefile 复制代码
field: value\n

field可以取四个值:"data", "event", "id", or "retry",也就是说有四类头信息。每次HTTP通信可以包含这四类头信息中的一类或多类。\n代表换行符。

以冒号开头的行,表示注释。 通常,服务器每隔一段时间就会向浏览器发送一个注释,保持连接不中断。

kotlin 复制代码
: this is a test stream\n\n
 
data: some text\n\n
 
data: another message\n
data: with two lines \n\n

data:数据栏

kotlin 复制代码
data: begin message\n
data: continue message\n\n

最后一行的data,结尾要用两个换行符号 \n\n,表示数据结束。

EventSource

EventSource 是服务器推送的一个网络事件接口。一个EventSource实例会对HTTP服务开启一个持久化的连接,以text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭。

五、具体实践

服务端实现,基于node
javascript 复制代码
const http = require('http');
const server = http.createServer((req, res) => {
  console.log('req',req.url)
  let fileName = '.' +req.url
  if(fileName === './sse') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
      'Access-Control-Allow-Origin': '*'
    })
    res.write('retry: 10000\n')
    res.write('event: connecttime\n')
    let interval = setInterval(() => {
      res.write(`data: ${new Date().toLocaleTimeString()}\n\n`);
    },1000)

    // 自定义事件
    req.connection.addListener(
      'close',
      () => {
        clearInterval(interval);
      },
      false
    )
  }

})
server.listen(3000, () => {
  console.log('Server is running at http://localhost:3000');
});
客户端实现
xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
  </head>
  <body>
    <div id="example"></div>
    <script >
      let source = new EventSource('http://localhost:3000/sse')
      let _div = document.getElementById('example')

      source.onopen = function (event){
        _div.innerHTML ='<p>开始连接...</p>'
      }
      source.onerror = function (event){
        _div.innerHTML ='<p>关闭连接...</p>'
      }

      source.onmessage = function (event) {
        _div.innerHTML += '<p>Ping: ' + event.data + '</p>'
      }
    </script>
  </body>
</html>
输出
相关推荐
豐儀麟阁贵12 小时前
8.5在方法中抛出异常
java·开发语言·前端·算法
zengyuhan50313 小时前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
醉方休13 小时前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running13 小时前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔13 小时前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户44455436542613 小时前
Android的自定义View
前端
WILLF13 小时前
HTML iframe 标签
前端·javascript
枫,为落叶13 小时前
Axios使用教程(一)
前端
小章鱼学前端13 小时前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah13 小时前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript