Server-Sent Events 一种轻量级的Push方式

2023-11-04


简单来说,Server-Sent Events(简称SSE)是服务端发送事件,即服务端Push的一种机制。

SSE工作原理

一般来说HTTP协议是要客户端先请求服务器,服务器才能响应给客户端,无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(event-streaming)。

也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

SSE 就是利用这种机制,使用流信息向客户端推送信息。

以浏览器和服务器为例,如下所示:

在这里插入图片描述

  1. 客户端请求建立事件流类型的连接,即Request Headers Accept = text/event-stream。

在这里插入图片描述

  1. 服务端响应请求,并将Response Headers Content-Type设置为text/event-stream,证明数据将以这种类型传送。
    在这里插入图片描述
  2. 服务端如果有数据就会发送给客户端。

SSE的特点

说到服务端推送,自然就想到WebSocket,那相比于WebSocket,SSE有哪些特点呢?

WebSocket SSE
全双工,可以同时发送和接收消息 单工,只能服务端单向发送消息
独立的协议 基于HTTP协议
协议相对复杂 协议相对简单,易于理解和使用
默认不支持断线重连 默认支持断线重连
默认支持传送二进制数据 一般只用来传送文本,二进制数据需要编码后传送
不支持自定义发送的数据类型 支持自定义发送的数据类型
支持CORS 不支持CORS,协议和端口都必须相同

SSE的推送数据格式

  • event: 事件类型,服务端可以自定义,默认是message事件

  • Id: 每一条事件流的ID,在失败重传事件流的时候有重要作用

  • retry: 浏览器连接断开之后重连的间隔时间,单位:毫秒,在自动重新连接的过程中,之前收到的最后一个事件流ID会被发送到服务端。

  • data: 发送的数据

每个字段K-V后面用"\n"结尾,如:
在这里插入图片描述
真正的数据用data字段表示,一般放到最后,使用"\n\n"结尾。

如果数据很长,可以分成多行,最后一行用\n\n结尾,前面行都用\n结尾。

data: begin message\n
data: continue message\n\n

如:

data: {\n
data: "name": "xujian",\n
data: "age", 18\n
data: }\n\n

SSE的使用

客户端

对于大部分浏览器都通过EventSource API 支持了SSE。

1、判断你的浏览器是否支持SSE:

if(typeof(EventSource)!=="undefined")
{
    alert('支持')
}
else
{
    alert('不支持')
}

2、如果支持,编写测试页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script>
var username = 'xujian';
// 创建EventSource对象,同时建立连接
<!--服务端使用SseEmitter时使用-->
var source = new EventSource('http://localhost:8080/sseEmitter/connect/' + username);
<!--服务端不使用SseEmitter时使用-->
<!--var source = new EventSource('http://localhost:8080/sseEmitter/data');-->

document.write(username + '正在连接...<br>');

// 监听连接建立完成事件
source.addEventListener('open', function (e) {
    document.write(username + '连接成功~<br>');
}, false);

// 监听连接错误事件
source.addEventListener('error', function (e) {
    document.write(username + '连接错误!<br>');
});

// 监听自定义消息推送事件,事件名称为“psh”,这个名字由服务端设置
source.addEventListener('psh', function (e) {
    document.write('收到消息:' + e.data + '<br>');
});
</script>
</html>

当然EventSource API也有对Java的支持。可以参考:https://github.com/launchdarkly/okhttp-eventsource

服务端

如果使用了Springboot,其内置了SseEmitter封装了对SSE的支持。

package com.example.springbootdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/sseEmitter")
public class SseEmitterController {
    private static final Map<String, SseEmitter> emitterMap = new HashMap<>();

    /**
     * 服务端不使用SseEmitter时使用
     *
     * @param response
     * @throws IOException
     */
    @GetMapping(value = "/data")
    public void getData(HttpServletResponse response) throws IOException {
        response.setContentType("text/event-stream;charset=UTF-8");
        response.getWriter().write("retry: 5000\n");
        response.getWriter().write("data: hahahaha\n\n");
        response.getWriter().flush();
        System.in.read();
    }

    /**
     * 服务端使用SseEmitter时使用
     *
     * @param username
     * @return
     * @throws IOException
     */
    @GetMapping(value = "/connect/{username}", produces = "text/event-stream;charset=UTF-8")
    public SseEmitter connect(@PathVariable String username) throws IOException {
        SseEmitter sseEmitter = new SseEmitter(0L);
        sseEmitter.onCompletion(() -> {
            System.out.println(username + "连接结束!");
            emitterMap.remove(username);
        });
        sseEmitter.onError((t) -> {
            System.out.println(username + "连接出错!错误信息:" + t.getMessage());
            emitterMap.remove(username);
        });
        sseEmitter.onTimeout(() -> {
            System.out.println(username + "连接超时!");
            emitterMap.remove(username);
        });
        emitterMap.put(username, sseEmitter);

        sseEmitter.send("连接建立成功");
        return sseEmitter;
    }

    /**
     * 服务端使用SseEmitter时使用
     *
     * @param username
     * @return
     * @throws IOException
     */
    @GetMapping(value = "/send/{username}")
    public String send(@PathVariable String username) throws IOException {
        SseEmitter sseEmitter = emitterMap.get(username);
        if (sseEmitter == null) {
            return "没查询到该用户的连接!";
        }
        sseEmitter.send(SseEmitter.event().name("psh").data("Hello~"));
        return "发送成功~";
    }

    /**
     * 服务端使用SseEmitter时使用
     *
     * @return
     * @throws IOException
     */
    @GetMapping(value = "/sendAll")
    public String sendAll() throws IOException {
        for (SseEmitter sseEmitter : emitterMap.values()) {
            sseEmitter.send(SseEmitter.event().name("psh").data("Hello~"));
        }
        return "发送完成~";
    }
}

如果不使用SseEmitter,那就需要自己按照固定格式构造SSE的响应数据。

效果展示

1)浏览器输入:http://localhost:8080/test.html
在这里插入图片描述
2)推送数据:http://localhost:8080/sseEmitter/send/xujian

3)浏览器接收到数据:
在这里插入图片描述


相关代码请参考:https://gitee.com/xujian01/blogcode/tree/master/springcode/src/main/java/com/jarry/sse

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Server-Sent Events 一种轻量级的Push方式 的相关文章

随机推荐

  • 代码走查

    转载 http forthtiger spaces live com Blog cns 95D3AD656DAA820A 175 entry b 为什么要执行代码走查 什么时候执行代码走查 如何有效执行代码走查 b 一般的看法 认为代码走查
  • C++数据类型(整型、浮点型、字符型、字符串型、布尔型)

    文章目录 1 整型 2 sizeof关键字 3 浮点型 实型 4 字符型 5 转义字符 6 字符串型 7 布尔类型 bool 8 C 数据类型小结 9 数据的输入 C 创建变量或常量时 必须指定数据类型 否则无法为变量分配内存 数据类型的意
  • 人工智能与机器学习著名会议

    转自论坛http www ieee org cn dispbbs asp BoardID 62 replyID 31567 id 29962 star 1 skin 0 作者好像是南大周志华老师 我知道的几个人工智能会议 一流 下面同分的按
  • 【华为OD机试真题 JAVA】字符串分割(二)

    JS版 华为OD机试真题 JS 字符串分割 二 标题 字符串分割 时间限制 1秒 内存限制 262144K 语言限制 不限 给定一个非空字符串S 其被N个 分隔成N 1的子串 给定正整数K 要求除第一个子串外 其余的子串每K个字符组成新的子
  • element ui 前台模板_一个干净优雅的Element UI Admin模板

    Element UI Admin 一个干净优雅的Element UI Admin模板 一个大型单页应用离不开合理的项目结构和一些简单的封装 Start 克隆或者下载这个仓库 进入项目目录安装依赖 yarn Develop serve wit
  • python:基于朴素贝叶斯算法的垃圾邮件过滤分类

    目录 一 朴素贝叶斯算法 1 概述 2 推导过程 二 实现垃圾邮件过滤分类 1 垃圾邮件问题背景 2 朴素贝叶斯算法实现垃圾邮件分类的步骤 3 python实现 参考学习网址 https blog csdn net weixin 59450
  • Nacos适配Oracle12c【亲测可用、保姆级教学】

    Nacos适配Oracle12c 前言 内容准备 数据SQL 源码项目 项目初始化 提取Nacos启动包 启动Nacos 尝试访问 其他问题 IDEA 报错找不到符号com alibaba nacos consistency entity
  • 华为OD机试 - 字符串加密(C++ & Java & JS & Python)

    描述 有一种技巧可以对数据进行加密 它使用一个单词作为它的密匙 下面是它的工作原理 首先 选择一个单词作为密匙 如TRAILBLAZERS 如果单词中包含有重复的字母 只保留第1个 将所得结果作为新字母表开头 并将新建立的字母表中未出现的字
  • 局域网IP地址不够用怎么办?快速解决局域网IP地址不够用

    目录 前言 设置局域网的IP地址数量 1 LAN地址设置 2 DHCP服务器设置 增加路由器层级或者使用软路由 通过三层交换机实现VLAN 总结 前言 在网络如此发达的时代 越来越多的设备需要连接网络 特别是当前智能家居 物联网的新型产业的
  • Maven 编译遇到 Process terminated【四种情况全部解决】

    情况一 配置文件 settings xml 出错 解决方法1 1 1 项目编译报错如下 1 2 点击 项目名 提示找到出错文件 1 3 点击查看出错文件 在idea中打开了settings文件 找到提示的报错位置 1 4 原因及解决办法 原
  • Altium中网络标签、端口、离图以及编译范围的设置区别?

    1 Altium Designer多图纸功能 原理图主次分层次 Altium Designer多图纸功能 原理图主次分层次 1 1 各类网络标识符 由于我们使用到多图纸功能 这时需要考虑图纸间的线路连接 在单个图纸中 我们可以通过简单的网络
  • pandas数据处理大全(必备)

    目录 文章目录 目录 pandas读取文件 pandas存储文件 pandas处理空值和缺失值 pandas创建空dataframe dataframe索引值的修改 dataframe选择行与列 dataframe转置 dataframe添
  • Nodejs 中的非阻塞I/O、异步和事件驱动

    前言 Node js使用 事件驱动机制 具有非阻塞的I O模型这样的特点 Node js中的大多数Api都用到了异步函数 那麽又该如何获取异步函数返回的数据呐 废话不多说 来看看本次分享 都有哪些亮点吧 一 Nodejs 的 非阻塞 I O
  • 使用VS2019 开发Linux C++ 程序

    第一步 注 如果Windows下面查询不到dns也可以不设置静态IP地址 直接跳到第二步 1 先将自己的Linux 系统设为静态IP 具体操作如下 修改 etc network interfaces 地址配置文件 如下所示 操作 代码 修改
  • 网络存储ISCSI详解

    一 网络存储 目前应用最为广泛的两种数据存储设备 NAS与SAN 1 NAS NAS的全称是Network attached storage 即网络附加存储 并不需要单独的网络用于存储IO 更适用于中小型的存储解决方案 NAS设备通常是一个
  • 关于PID的输入输出是什么--供自己复习使用

    本人也是个新手 最近对平衡车感兴趣 所以恶补了一些关于pid的知识 下面是关于pid的文章 后续在平衡车上有进展也会出一些关于平衡车的文章 第一次写文章 有许多的不足之处 希望给位网友给予指正 在过程控制中 按偏差的比例 P 积分 I 和微
  • Linux文件解压缩:tar: /xxx: Not found in archive

    因为压缩文件使用的相对路径 如果在解压是使用绝对路径需要加上 C指令 tar zxvf xxx tar gz C usr local apps
  • 5G到底厉害在什么地方?和4G有什么不同?

    5G给大家的第一个印象肯定就是速度很快 秒下电影 这也是媒体大力宣传的一个点 5G的速度的确比4G快了很多 但是5G不仅仅是速度大大提高了 还有其他更重要的优点 不知道你有没有这种感觉 4G的速度已经挺快了啊 我们手机看视频一点都不卡啊 有
  • mpvue中小程序版本更新

    在App vue中 onLaunch if wx canIUse getUpdateManager const updateManager wx getUpdateManager updateManager onCheckForUpdate
  • Server-Sent Events 一种轻量级的Push方式

    文章目录 SSE工作原理 SSE的特点 SSE的推送数据格式 SSE的使用 客户端 服务端 效果展示 简单来说 Server Sent Events 简称SSE 是服务端发送事件 即服务端Push的一种机制 SSE工作原理 一般来说HTTP