使用SSE(Server-Sent Events)实现服务端给客户端发消息

2023-09-21 17:08:19

首先是客户端,看着比较简单。但实际应用中可能要比这复杂,因为默认sse只支持get请求,而且没法携带header。所以如果默认的方法达不到需求的话可能需要额外实现,当然也可以引用第三方库,比如@rangermauve/fetch-event-source。所以有谁会自己实现呢?

if(typeof(EventSource)!=="undefined")
{
    var source=new EventSource("http://localhost:9001");
	source.onopen = function(event) {
  		console.log("onopen")
	};
	source.onmessage=function(event)
	{
		console.log("onmessage");
		console.log("data is ", event.data);
		document.getElementById("result").innerHTML=event.data + "<br>";
	};
}
else
{
	// 浏览器不支持 server-sent 事件
}

服务端java代码如下:

package com.zhouz.signing.controller.v1;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

@RestController
@CrossOrigin
public class TestController {
    protected static Logger logger = LoggerFactory.getLogger(UserController.class);

    @RequestMapping(value = "/testsse")
    @ResponseBody
    public void getStreamDataImprove(HttpServletResponse httpServletResponse) {
        httpServletResponse.setContentType("text/event-stream"); // content-type必须是text/event-stream
        httpServletResponse.setCharacterEncoding("utf-8"); // 编码必须是utf-8
        // 这里用死循环是为了和客户端建立长连接
        while (true) {
            String s = "retry:10000\n"; // retry:后面跟单位为毫秒的数字,客户端会在断开连接后按照设置的毫秒数进行重连
            String i = new Date().toString();
            s += "id:" + i + "\n"; // id: 设置id,可以在比如客户端网络错误的时候下一次再连接时向服务端发送的请求中header中带有Last-Event-Id参数,服务端拿到这个值就可以将未推送的数据再次推送给客户端
            s += "data:" + i + "\n\n"; // data: 设置数据,注意一则消息的最后必须要有两个换行符
            try {
                PrintWriter pw = httpServletResponse.getWriter();
                Thread.sleep(1000L); // 如果不想给客户端发送消息过于频繁,可以设置等待时间
                pw.write(s);
                pw.flush();
                if (pw.checkError()) {
                    logger.info("客户端断开连接");
                    break; // 判断出错后,需要结束死循环,本次请求也就结束了。
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

服务端php代码

<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: *");
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

while(true) {
	$time = date('r');
	echo "data: {$time}\n\n";
    ob_flush();
    flush();
    sleep(1);

    // TODO 这里不知道是否可以用这个方法来判断客户端断开了连接
    if (connection_aborted()) {
    	break;
    }
}

需要注意的是,如果服务端不加死循环,前端看着是3秒发起一个请求。而加了死循环之后,前端实际上只发送了一次请求。

更多推荐

如何在Ubuntu系统部署RabbitMQ服务器并公网访问【内网穿透】

文章目录前言1.安装erlang语言2.安装rabbitMQ3.内网穿透3.1安装cpolar内网穿透(支持一键自动安装脚本)3.2创建HTTP隧道4.公网远程连接5.固定公网TCP地址5.1保留一个固定的公网TCP端口地址5.2配置固定公网TCP端口地址前言RabbitMQ是一个在AMQP(高级消息队列协议)基础上完

区块链技术:解密去中心化的革命

文章目录区块链的基础概念什么是区块链?区块链的核心原理1.分布式账本2.区块3.加密技术4.共识机制区块链的工作原理区块链的交易过程区块链的安全性共识机制的作用区块链的应用领域1.金融服务2.供应链管理3.物联网4.医疗保健5.政府与公共服务区块链的未来展望1.去中心化金融2.物联网的安全性3.智能合约的广泛应用4.数

数字森林:无人机航测技术在林业调查中的应用

林业调查是林业工作的基础,对于森林资源的管理、规划、保护、经济发展和农业种植等方面都具有重要的意义。传统林业调查主要依赖人工进行,存在工作效率低、数据精度低、数据分析困难、受地形限制、无法实时监测等缺陷。随着科技的不断发展,无人机作为一种高效、灵活的工具,正在越来越多的领域得到应用。无人机航测利用无人机搭载的航摄设备,

数据通信——传输层TCP(超时时间选择)

引言TCP每一次发送报文段,就会对这个报文段设置一次计时器。如果时间到了却没有收到确认报文,那么就要重传该报文。这个之前在TCP传输的机制中提到过,这个章节就来研究一下超时时间问题。关于加权的概念有必要提及一下加权的概念,这属于数学知识,但可用帮助我们理解超时重传机制。权是在测量时不同的精准度,加权就是乘上权重/系数的

并发编程——JUC并发工具

文章目录前言CountDownLatchCountDownLatch应用CountDownLatch核心源码SemaphoreSemaphore应用Semaphore核心源码CyclicBarrierCyclicBarrier应用CyclicBarrier核心源码总结前言JUC是Java并发编程工具类库,提供了一些常用

Vue3 实现一个无缝滚动组件(支持鼠标手动滚动)

Vue3实现一个无缝滚动组件(支持鼠标手动滚动)前言在日常开发中,经常遇到需要支持列表循环滚动展示,特别是在数据化大屏开发中,无缝滚动使用频率更为频繁,在jquery时代,我们常用的无缝滚动组件为liMarquee,在vue中已经有vue-seamless-scroll组件(通过Vue2实现,不支持鼠标手动滚动),但是

LeetCode算法递归类—剑指 Offer 26. 树的子结构

目录剑指Offer26.树的子结构题解:代码:运行结果:​编辑输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构,即A中有出现和B相同的结构和节点值。例如:给定的树A:3/\45/\12给定的树B:4/1返回true,因为B与A的一个子树拥有相同的结构和节点值。示例1:输入:

10个强大的 JavaScript 动画库、直接抄作业

动画,是吸引你客户注意的好方法之一。在项目开发中,我们可以通过创造有趣的动画来为我们的项目增加视觉感与用户体验,同时,也为我们的网站增添了独特的美感,而且还提高了用户参与度并创造了令人难忘的第一印象。因此,今天这篇文章,我将整理了10个有趣又有用的JavaScript动画库,从而帮助你快速创建动画,其中包括,滚动动画、

基于Hadoop的豆瓣电影的数据抓取、数据清洗、大数据分析(hdfs、flume、hive、mysql等)、大屏可视化

目录项目介绍研究背景国内外研究现状分析研究目的研究意义研究总体设计数据获取网络爬虫介绍豆瓣电影数据的采集数据预处理数据导入及环境配置Flume介绍Hive介绍MySQL介绍Pyecharts介绍环境配置及数据加载大数据分析及可视化豆瓣影评结构化分析豆瓣电影类型占比分析豆瓣电影导演排行榜分析不同国家的电影数据分析电影演员

PostgreSQL 主从复制&故障切换

文章目录前言环境准备搭建主从节点配置主从节点从节点加入主节点查看主从信息主从故障切换前言PostgreSQL的主从复制是一种非常简单且常用的高可用性和可扩展性解决方案,本质上是将主服务器的数据复制到一个或多个从服务器上,从而提高系统的性能和可靠性,并提供数据备份和故障恢复的能力。环境准备搭建主从节点准备两个节点,一主一

@Deprecated

@Deprecated是一个Java中的注解,用于指示某个类、方法、字段或者其他程序元素已经被废弃不再建议使用。使用@Deprecated注解可以向其他开发者传达这个元素已经过时,不推荐使用,并提醒他们使用其他替代的方案。通常情况下,当一个类、方法、字段或者其他程序元素被@Deprecated注解标记之后,编译器会在使

热文推荐