编程 不止 WebSocket 可以实现长连接,它也可以:Server-Sent Events(SSE)

2024-11-19 02:59:49 +0800 CST views 984

不止 WebSocket 可以实现长连接,它也可以:Server-Sent Events(SSE)

在前端开发中,服务端消息推送通常会使用 WebSocket,特别是在聊天室应用场景中。虽然 WebSocket 提供了双向通信的能力,但并不是唯一的长连接解决方案。Server-Sent Events(SSE)也可以用于基于服务端的消息推送,但它是单向的,数据只能从服务端发送到客户端。

例如,像 ChatGPT 这样的应用场景下,文本输出就是基于服务器的消息推送来进行数据的实时输出。本文将介绍如何使用 EventSource 实例来实现与服务端的通信,并实现消息推送。

1. 什么是 EventSource

EventSource 是一个用于接收服务端推送消息的实例,它基于 HTTP 的长连接,能够始终保持开启状态,直到调用 close 方法关闭连接。需要注意的是,EventSource 不支持通过 axios 进行实现,因为 axios 使用的是 XMLHttpRequest,无法处理服务端推送消息。

后端服务示例

首先,我们需要实现一个简单的后端服务,使用 SSE 推送数据给前端。下面是一个基于 Node.js 的示例:

const article = `警告:当不使用 HTTP/2 时.....。`
app.get('/chat_typing', (req, res) => {
  // 开启 Server-sent events
  res.setHeader('Content-Type', 'text/event-stream')
  let index = 0
  let timerId = 0
  // 模拟每隔 0.1s 向前端推送一次
  timerId = setInterval(() => {
    // 获取文字
    const data = article[index]
    // 下标累加
    index++
    // 响应结果
    if (data) {
      // data:表示数据内容,\n\n 表示结尾。
      res.write(`data: ${data}\n\n`)
    } else {
      res.end()
      clearInterval(timerId)
    }
  }, 100)
})

在这个示例中,服务器每 100 毫秒向前端推送一个字符,Content-Type 设置为 text/event-stream,表示这是 SSE 消息推送。

前端接收消息

在前端,我们可以通过 EventSource 实例来建立长连接,并接收服务端的消息推送。以下是基于 Vue.js 的示例:

<script setup>
import { ref } from 'vue'

const article = ref('')
let source
const OpenSSE = () => {
  source = new EventSource('http://localhost:3000/chat_typing')
  // 接收信息
  source.addEventListener('message', (e) => {
    // 实时输出字符串
    article.value += e.data
  })
}
</script>

<template>
  <div>
    <button @click="OpenSSE">开启SSE</button>
    <div>{{ article }}</div>
  </div>
</template>

当点击按钮时,前端将会开启与服务器的连接,并通过 EventSource 实例监听服务器发送的消息。在接收到的消息中,每个字符会逐步显示在页面上。

关闭连接

如果我们需要关闭这个长连接,可以调用 EventSource 实例的 close 方法:

const CloseSSE = () => {
  source.close()
}

2. 使用 fetch 优化连接

虽然 EventSource 适用于简单的消息推送场景,但它只支持 GET 请求,并且参数只能通过 URL 拼接传递。如果需要传递更多上下文或使用 POST 请求,那么 fetch 可以更好地处理这种情况。

基于 fetch 的长连接

以下是使用 fetch 来代替 EventSource 实现连接和数据推送的代码示例:

const OpenSSE = async () => {
  const res = await fetch('http://localhost:3000/chat_typing')
  console.log(res)
}

为了终止请求,我们可以使用 AbortController 来控制请求的终止操作:

const abort = new AbortController()
const OpenSSE = async () => {
  const res = await fetch('http://localhost:3000/chat_typing', {
    signal: abort.signal,
  })
  console.log(res)
}
const CloseSSE = () => {
  abort.abort()
}

AbortController 可以用来终止未完成的异步操作,在长连接中非常有用。

解析返回的流

通过 fetch 获取服务器的流数据,接着我们可以使用 res.body.getReader() 读取数据流。getReader() 返回一个 Promise,我们可以逐块读取流数据。

const OpenSSE = async () => {
  const res = await fetch('http://localhost:3000/chat_typing', {
    signal: abort.signal,
  })
  const content = res.body.getReader()
  const decoder = new TextDecoder()
  while (content) {
    const { done, value } = await content.read()
    if (done) break
    console.log(decoder.decode(value))
  }
}

content.read() 是异步操作,返回的 valueUint8Array 格式的数据,需要通过 TextDecoder 转换为字符串。

支持 POST 请求的示例

在某些场景下,我们可能需要通过 POST 请求来传递更多的上下文信息。以下是修改为 POST 请求的示例:

后端:

app.post('/chat_typing', (req, res) => {
  // 处理 POST 请求
  res.write({ data: 'some data' })
})

前端:

const OpenSSE = async () => {
  const res = await fetch('http://localhost:3000/chat_typing', {
    method: 'POST',
    signal: abort.signal,
  })
  const content = res.body.getReader()
  const decoder = new TextDecoder()
  while (content) {
    const { done, value } = await content.read()
    if (done) break
    console.log(decoder.decode(value))
  }
}

3. 总结

本文展示了如何使用 EventSourcefetch 来实现服务器消息推送的长连接机制。

  • EventSource:适用于简单的消息推送场景,易于使用,但只支持 GET 请求,且参数只能通过 URL 传递。
  • fetch:提供了更多的灵活性,支持 POST 请求、传递参数,并可以处理数据流。

SSE 和 fetch 都可以用于实现长连接,常用于消息推送、提醒等功能。

复制全文 生成海报 前端开发 网络通信 实时数据推送

推荐文章

Elasticsearch 的索引操作
2024-11-19 03:41:41 +0800 CST
阿里云免sdk发送短信代码
2025-01-01 12:22:14 +0800 CST
pycm:一个强大的混淆矩阵库
2024-11-18 16:17:54 +0800 CST
浅谈CSRF攻击
2024-11-18 09:45:14 +0800 CST
MySQL用命令行复制表的方法
2024-11-17 05:03:46 +0800 CST
Gin 与 Layui 分页 HTML 生成工具
2024-11-19 09:20:21 +0800 CST
thinkphp分页扩展
2024-11-18 10:18:09 +0800 CST
Nginx 实操指南:从入门到精通
2024-11-19 04:16:19 +0800 CST
批量导入scv数据库
2024-11-17 05:07:51 +0800 CST
Plyr.js 播放器介绍
2024-11-18 12:39:35 +0800 CST
跟着 IP 地址,我能找到你家不?
2024-11-18 12:12:54 +0800 CST
Flet 构建跨平台应用的 Python 框架
2025-03-21 08:40:53 +0800 CST
html文本加载动画
2024-11-19 06:24:21 +0800 CST
markdowns滚动事件
2024-11-19 10:07:32 +0800 CST
Vue中的异步更新是如何实现的?
2024-11-18 19:24:29 +0800 CST
开源AI反混淆JS代码:HumanifyJS
2024-11-19 02:30:40 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
Node.js中接入微信支付
2024-11-19 06:28:31 +0800 CST
Nginx 防盗链配置
2024-11-19 07:52:58 +0800 CST
在Vue3中实现代码分割和懒加载
2024-11-17 06:18:00 +0800 CST
程序员茄子在线接单