Server-Sent Events(SSE)简单实现实时通信

Server-Sent EventsSSE)是一种基于HTTP的实时通信协议,它允许服务器向客户端推送信息。相比于传统的轮询方式,SSE 提供了更加轻量级和实时的通信机制。在本文中,我们将深入浅出地介绍如何简单实现 Server-Sent Events,以便在你的应用程序中实现实时通信。

SSE 实时通信

一、什么是 Server-Sent Events?

Server-Sent Events 允许服务器单向的向客户端发送事件流。这些事件流以纯文本形式传输,使用简单的文本格式,如data: message\n\n。客户端通过监听这些事件流,可以在接收到新事件时执行相应的操作。SSE 的一大优势在于它建立在标准的 HTTP 协议之上,不需要额外的握手和连接管理。

SSE 特点

  1. 简单性:SSE构建在HTTP协议之上,这意味着您无需引入额外的库或协议就可以开始使用。对于那些希望保持代码库干净并避免复杂性的开发人员来说,这是一个巨大的优势。
  2. 单向通信:SSE是一种单向通信协议,意味着数据只能从服务器流向客户端。这种单向性适用于各种情况,例如实时新闻更新、股票价格变动等,这些场景中,只有服务器需要推送数据。
  3. 自动重连:SSE连接在意外断开时会自动尝试重新连接。这种机制确保了在网络故障或连接中断后能够及时恢复通信,为用户提供连续的数据流。
  4. 事件流:SSE使用"事件流"(event stream)将数据从服务器发送到客户端。每个事件都可以包含一个事件标识符、事件类型和数据字段。客户端可以根据这些信息来解析和处理接收到的数据。

二、实现 Server-Sent Events

要实现 Server-Sent Events,我们需要创建一个 HTTP 服务器,负责向客户端推送事件流。以下是一个使用 Node.js 和 Next.js 框架的简单示例:

// pages/api/sse.js

import { nanoid } from 'nanoid'

export default function GET(req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream; charset=utf-8',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive',
  })
  // res.status(200).json({ text: 'Hello' })
  res.write(
      `data: ${JSON.stringify({
        id: 1,
        message: 'Hello, world!',
        timestamp: new Date().toISOString(),
      })}\n\nid: 1\n\n`
  )
  res.write(`event: update\ndata: bye-bye\n\nid: ${nanoid(6)}\n\n`)
  res.write('event: hi\ndata: hi\n\nid: 100\n\n')
  let n = 0
  let t = setInterval(() => {
    n++
    if (n > 5) {
      clearInterval(t)
      res.end('event: end\ndata: 结束通信。\n\nid: 9999\n\n')
    } else {
      let id = nanoid(6)
      let data = JSON.stringify({
        id,
        message: `hi,这是第 ${n} 条消息。`,
        timestamp: new Date().toISOString(),
      })
      res.write(`data: ${data}\n\nid: ${id}\n\n`)
    }
  }, 1000)
}

在这个示例中,我们创建了一个 /events 的路由,该路由返回 SSE 格式的数据。客户端可以通过访问 /events 路径来获取实时的事件流。服务器每隔一秒向客户端发送当前时间,你可以根据实际需求修改发送的数据。

三、在客户端使用 Server-Sent Events

客户端可以使用 JavaScript 中的 EventSource 对象来监听服务器发送的事件。以下是一个简单的 HTML 示例:

// pages/sse/index.jsx

import { useReactive } from 'ahooks'
import { Button, List, Space, Typography } from 'antd'
import { useRef } from 'react'

export const config = {
  maxDuration: 30,
}

export default function Page() {
  const sse = useRef()
  const that = useReactive({
    list: [],
  })

  function connect() {
    init()
  }

  function close() {
    sse.current?.close()
  }

  function init() {
    sse.current = new EventSource('/api/sse', {withCredentials: true})

    // 连接一旦建立,就会触发open事件,可以在onopen属性定义回调函数。
    sse.current.onopen = function (event) {
      // console.log(event)
      // console.log(sse.current.CONNECTING)
      // console.log(sse.current.OPEN)
      // console.log(event.lastEventId)
    }

    // 客户端收到服务器发来的数据,就会触发message事件,
    // 可以在onmessage属性的回调函数。
    sse.current.onmessage = function (event) {
      // console.log('onmessage:')
      let {lastEventId, data, type} = event
      console.log({lastEventId, data, type})
      that.list.push({lastEventId, data})
    }

    sse.current.addEventListener('update', function (event) {
      // console.log('onupdate:')
      let {lastEventId, data, type} = event
      console.log({lastEventId, data, type})
    })

    sse.current.addEventListener('hi', function (event) {
      // console.log('onhi:')
      let {lastEventId, data, type} = event
      console.log({lastEventId, data, type})
    })

    sse.current.addEventListener('end', function (event) {
      // console.log('onhi:')
      let {lastEventId, data, type} = event
      console.log({lastEventId, data, type})
      close()
    })

    // 如果发生通信错误(比如连接中断),就会触发error事件,
    // 可以在onerror属性定义回调函数。
    sse.current.onerror = function (event) {
      // handle error event
    }
  }

  function clear() {
    that.list = []
  }

  return (
      <div className="w-100% flex flex-col justify-center items-center">
        <Space size="middle" style={{display: 'flex'}}>
          <Button onClick={close} danger>
            断开连接
          </Button>
          <Button onClick={connect} type="primary">
            建立连接
          </Button>
        </Space>
        <br/>
        <List
            header={
              <div className="flex justify-between">
                通信结果
                <Button size="small" onClick={clear}>
                  清空
                </Button>
              </div>
            }
            footer={null}
            bordered
            style={{width: '100%'}}
            dataSource={that.list}
            renderItem={({lastEventId, data}) => (
                <List.Item>
                  <Typography.Text mark>[{lastEventId || 0}]</Typography.Text> {data}
                </List.Item>
            )}
        />
      </div>
  )
}

在这个示例中,我们使用 EventSource 对象连接到 /events 路径,然后监听 message 事件来处理服务器发送的数据。当连接关闭时,我们还监听了 error 事件以处理连接关闭的情况。
通过这样的简单实现,你可以在应用程序中实现实时通信,为用户提供更加即时和动态的体验。 Server-Sent Events 是一个强大而简单的工具,适用于多种场景,如实时聊天、通知推送等。

参考文档:

  • https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource
  • https://mp.weixin.qq.com/s/7oaRLZD9p343bZLRynCcKA
  • https://zh.javascript.info/server-sent-events#wan-zheng-shi-li
  • https://mp.weixin.qq.com/s/7oaRLZD9p343bZLRynCcKA

欢迎访问:天问博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/345619.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在上海做程序员这么多年,退休后我的工资是多少?

大家好&#xff0c;我是拭心。 最近看到一个很可惜的事&#xff1a;有个阿姨在深圳缴纳了 12 年社保&#xff0c;第 13 年家里突然有事不得不回老家&#xff0c;回去后没再缴纳社保&#xff0c;结果退休后无法领退休工资&#xff0c;还得出来打工赚钱。 之所以这样&#xff0…

STL常用容器—stack与queue容器(栈与队列)

STL常用容器—stack与queue容器&#xff08;栈与队列&#xff09; stack容器1. stack容器模型图2. stack 基本概念3. stack 常用接口 queue 容器1. queue 容器模型图2. queue 基本概念3. queue 常用接口 参考博文1&#xff1a;&#xff1c;C&#xff1e; stack与queue容器概念模…

这种环境下腾讯64亿在北京拿地?

近期&#xff0c;金融市场出现较大波动&#xff0c;A股指数跌至2700点&#xff0c;同时恒生指数也下滑至15000点&#xff0c;引发了社会各界的关注和思考。与此同时&#xff0c;腾讯以64.2亿元拿下北京海淀区地块&#xff0c;马云和蔡崇信又增持阿里股票&#xff0c;这一系列的…

【Java网络编程01】网络原理初识

【Java网络编程01】网络原理初识 1. 网络通信基础概念 网络通信&#xff1a;网络互连的目的就是网络通信&#xff0c;即网络数据传输&#xff0c;再直白点而言就是不同主机的不同进程之间基于网络进行数据的传输交互。 那么&#xff0c;在组建的网络上有各种各样的主机&#…

【Conda】超详细的linux-conda环境安装教程

背景 最近被python各个版本环境整的头晕目眩&#xff0c;本来就不是专长做python的&#xff0c;切换各种版本着实不好操作&#xff0c;因此想到了conda这个好工具&#xff0c;以下是对conda的相关理解和搭建的详细过程&#xff0c;做个记录。 Conda简介 Conda是在Windows、m…

3.Eureka注册中心

3.Eureka注册中心 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 大家思考几个问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址和端口&#xff1f;有多个user-service实例地址&#xff0…

Redis - redis.windows.conf配置文件及RDB和AOF数据持久化方案

Redis - redis.windows.conf配置文件及RDB和AOF数据持久化方案 Redis的高性能是由于其将所有数据都存储在了内存中&#xff0c;为了使Redis在重启之后仍能保证数据不丢失&#xff0c;需要将数据从内存中同步到硬盘中&#xff0c;这一过程就是持久化。 Redis支持两种方式的持久化…

Vue 的 事件修饰符and按键修饰符

1、事件修饰符概览 修饰符说明 .prevent阻止默认事件 .stop阻止冒泡.once事件只触发一次 .capture 添加事件侦听器时使用事件捕获模式.self只有点击当前元素本身时才会触发回调.passive事件的默认行为立即执行&#xff0c;无需等待事件回调执行完毕(不常用).native 将vue组件…

【单例模式】保证线程安全实现单例模式

&#x1f4c4;前言&#xff1a;本文是对经典设计模式之一——单例模式的介绍并讨论单例模式的具体实现方法。 文章目录 一. 什么是单例模式二. 实现单例模式1. 饿汉式2. 懒汉式2.1 懒汉式实现单例模式的优化&#xff08;一&#xff09;2.2 懒汉式实现单例模式的优化&#xff08…

蓝桥杯官网填空题(01串的熵)

问题描述 答案提交 这是一道结果填空的题, 你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。 import java.util.*;public class Main {public static void main(String[] args) {for(double zero1;zero<2333…

开始学习vue2(Vue方法)

一、过滤器 过滤器&#xff08;Filters&#xff09;是 vue 为开发者提供的功能&#xff0c;常用于文本的格式 化。过滤器可以用在两个地方&#xff1a;插值表达式 和 v-bind 属性绑定。 过滤器应该被添加在 JavaScript 表达式的尾部&#xff0c;由“管道符 ”进行 调用&#…

【Linux】常见指令(二)

前言 常见指令第二部分。 文章目录 一、指令&#xff08;下&#xff09;重定向>&#xff1a;输出重定向>>&#xff1a;追加输出<&#xff1a;输入重定向 10. more—显示文本文件内容11.less—逐屏浏览文本文件内容12. head13. tail管道 |14. date—时间指令在这里插…

5个程序员可以接私活的平台和一些建议

22年之前我从没有接触过程序员外包接单&#xff0c;也没有任何的私活接单经验&#xff0c;就纯纯看自己瞎摸索&#xff0c;通过Google搜索&#xff0c;在各类程序员私活接单平台上摸爬滚打&#xff0c;硬是杀出一条血路&#xff0c;从一开始的年入3k到现在每月稳定收入1w&#…

STL第四讲

第四讲 万用Hash Function 左侧的是设计为类并重载调用运算符&#xff0c;右侧是一般函数的形势&#xff1b; 但是右侧形势在创建容器时更麻烦&#xff1b; 具体例子&#xff1a; 第三种形势&#xff1a;struct hash 偏特化形式 tuple 自C03引入&#xff1b; 关于源码解读的…

Xcode 15 libarclite 缺失问题

升级到Xcode 15运行项目报错&#xff0c;报错信息如下&#xff1a; SDK does not contain libarclite at the path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a; try increasing the minimum d…

2024年学鸿蒙开发有前途吗?

随着科技的不断发展和智能设备的普及&#xff0c;鸿蒙系统作为华为自主研发的操作系统&#xff0c;正逐渐受到市场的关注。2024年&#xff0c;学鸿蒙开发是否有前途&#xff0c;成为了很多开发者和学生关心的问题。本文将从多个角度分析鸿蒙系统的发展前景&#xff0c;以及学习…

elment-plus如何引入scss文件实现自定义主题色

elment-plus如何引入scss文件实现自定义主题色&#xff01;如果您想修改elementPlus的默认主题色调&#xff0c;使用自定义的色调&#xff0c;可以考虑使用官方提供的解决办法。 第一步你需要在项目内安装sass插件包。 npm i sass -D 如图&#xff0c;安装完成后&#xff0c;你…

Redisson 分布式锁解决主从一致性问题的原理

目录 一、主从不一致产生原因 二、Redisson 解决主从一致性的原理 一、主从不一致产生原因 1. Redis 主从集群&#xff1a;主从读写分离&#xff0c;主节点将数据同步给从节点 主节点&#xff1a;增删改从节点&#xff1a;读 2. 主从同步存在延迟&#xff0c;若主节点宕机…

Borze(与连续元素有关的题目)

总结&#xff1a;碰到与连续元素有关的题目&#xff0c;可以考虑在某种条件下加i&#xff1b;之类的。