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>
输出
相关推荐
神仙别闹1 小时前
基于VUE+Node.JS实现(Web)学生组队网站
前端·vue.js·node.js
下雨的Jim1 小时前
前端速成之——Script
前端·笔记
Captaincc1 小时前
使用 Copilot 代理模式构建着陆页
前端·ai编程·github copilot
zyk_5202 小时前
前端渲染pdf文件解决方案-pdf.js
前端·javascript·pdf
Apifox.2 小时前
Apifox 4月更新|Apifox在线文档支持LLMs.txt、评论支持使用@提及成员、支持为团队配置「IP 允许访问名单」
前端·人工智能·后端·ai·ai编程
划水不带桨2 小时前
大数据去重
前端
沉迷...2 小时前
手动实现legend 与 echarts图交互 通过js事件实现图标某项的高亮 显示与隐藏
前端·javascript·echarts
可观测性用观测云2 小时前
观测云数据在Grafana展示的最佳实践
前端
uwvwko3 小时前
ctfhow——web入门214~218(时间盲注开始)
前端·数据库·mysql·ctf
Json____3 小时前
使用vue2开发一个医疗预约挂号平台-前端静态网站项目练习
前端·vue2·网站模板·静态网站·项目练习·挂号系统