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>
输出
相关推荐
蓝胖子的多啦A梦3 分钟前
Vue+css实现扫描动画效果(使用@keyframes scan)
前端·css·vue.js·keyframes scan
沐土Arvin19 分钟前
Web 安全进阶:前端信封加解密技术详解
前端·javascript·安全·设计模式
码上敲享录35 分钟前
前端如何播放flv视频
前端·音视频
shenyan~1 小时前
关于 Web 安全:4. 中间件 & 框架风险点分析
前端·安全·中间件
wwf12251 小时前
css 里面写if else 条件判断
前端·css
Magnum Lehar2 小时前
vulkan游戏引擎的renderer下的vulkan缓冲区实现
java·前端·游戏引擎
_CodePencil_2 小时前
CSS专题之flex: 1常见问题
前端·css·html·css3·html5
哎呦你好2 小时前
CSS 文字样式全解析:从基础排版到视觉层次设计
前端·css
哎呦你好2 小时前
CSS 链接样式全解析:从基础状态到高级交互效果
前端·css
花菜会噎住2 小时前
网页 CSS美化2(详解)
前端·css·html·基础·网页