实时通讯技术Ajax,WebSocket,SSE

news2025/6/15 17:58:52

实时通讯技术是一项基于web开发的重要技术,网站是需要前后端通讯的,因此数据刷新的时间就是获取信息的时间,为了能准确而有快速的获取信息需要尽可能的提高信息的刷新效率。

常见的实时通讯技术:

通讯方式AjaxCometWebSocketSSE
描述短轮询是浏览器端提交表单查询长轮询是服务器收到请求后如果有数据, 立刻响应请求; 如果没有数据就会 等待 一段时间,直到有数据后立刻响应请求; 如果时间到了还没有数据, 则响应 http 请求(定时刷新)WebSocket的实现了一次连接,双方通信的功能。首先由客户端发出WebSocket请求,服务器端进行响应,TCP三次握手。这个连接一旦建立起来,就保持在客户端和服务器之间,两者之间可以直接的进行数据的互相传送。在 sse 的场景下,客户端发起请求,连接一直保持,服务端有数据就可以返回数据给客户端,这个返回可以是多次间隔的方式。sse 是单通道,只能服务端向客户端发消息
通讯协议httphttpwebsockethttp

Ajax实现方案

Ajax及时通讯是比较方便实现的,通过浏览器DOM元素设定页面定时刷新就可以动态获取后端的数据。

  • 添加setinterval()函数
<script language="javascript">
	setInterval(function(){
		window.location.reload();
	},3000);   //每隔3000毫秒刷新一次
</script>
  • mata添加content元素间隔刷新
<meta http-equiv="refresh" content="20">
  • 定时跳转转页面
<mata http-equiv="refresh" content"3,url=#">

//定时跳转也会刷新数据

通过js的动态刷新技术,再在刷新同添加Ajax通讯技术就可实现动态刷新了。

基于Ajax的长轮询方式


	/*
    setInterval(() => {
        mychart2.clear();
        axios({
          method:'get',
          url:'http://localhost:8100/json',
        }).then(function (response) {
			console.log(response)
			
          temp=response.data
        })
	
        i=i+1;
        option.xAxis.data.push(i);
        option.xAxis.data.shift();
        option.series[0].data.push(temp.data);
        option.series[0].data.shift();
		
        mychart2.setOption(option);
    }, 3000);
	*/
	

上函数通过在定时刷新里面添加了axios技术在间隔时间内查询数据并添加到列表中以实现动态刷新。如下图所示:请添加图片描述

这种Ajax长轮询技术(Comet)的缺点是无法满足即时通信等富交互式应用的实时更新数据的要求。

基于Iframe的长轮询方式

该方法就是返回一个新的html,标记为src属性的变化。服务端将返回的数据作为回调函数的参数,浏览器在收到数据后就会执行脚本。

WebSocket实现方案

websocket通过握手连接,形成半双工的全通道通讯,由服务器和客户端都可以主动互相通讯。WebSocket可以让服务器直接给客户端发送信息,而不是先等客户端发起请求后、服务器才返回信息。(比起轮询,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

WebSocket官方网站

后台编写:

package com.example;



import java.io.IOException;
import java.util.logging.Logger;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;



/**
 * @Class: Test
 * @Description: 简单websocket demo
 */
@ServerEndpoint(value="/websocketTest/{userId}")
public class WsTest {

    private Logger logger = Logger.getLogger("WebSocket");

    private static String userId;

    int i=0;

    //连接时执行
    @OnOpen
    public void onOpen(@PathParam("userId") String userId,Session session) throws IOException{
        this.userId = userId;
        logger.info("有新的链接!");
        System.out.println("新连接:"+userId);
    }

    //关闭时执行
    @OnClose
    public void onClose(){

        logger.info("有链接关闭!");

        System.out.println("连接:"+this.userId);
    }

    //收到消息时执行
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println("收到用户"+this.userId+"的消息"+message);
        //session.getBasicRemote().sendText("服务器 "+this.userId+" 发送来一条消息的消息 "); //回复用户
        i++;
        session.getBasicRemote().sendText(String.valueOf(i));  //返回数据
    }

    //连接错误时执行
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("用户id为:"+this.userId+"的连接发送错误");
        error.printStackTrace();
    }


}

前端编写:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
websocket Demo---- user000 <br />
<input id="text" type="text" />
<button onclick="send()"> Send </button>
<button   onclick="closeWebSocket()"> Close </button>
<div id="message">   </div>

<script type="text/javascript">
    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8080/demo/websocketTest/user000");
        console.log("link success")
    }else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }
    console.log("-----")
    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }

    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>

</body>
</html>

启动tomcat服务器访问websocket前端界面后返回数据:

在这里插入图片描述

SSE实现方案

SseEmitter的用法是使用 HTTP 做服务端数据推送应用的技术。SSE ( Server-sent Events )是 WebSocket 的一种轻量代替方案,使用 HTTP 协议。

SSE 和 WebSocket 做的是同一件事情。当你需要用新数据局部更新网络应用时,SSE 可以做到不需要用户执行任何操作,便可以完成。SSE 是单向通道,只能服务器向客户端发送消息,如果客户端需要向服务器发送消息,则需要一个新的 HTTP 请求。 这对比 WebSocket 的双工通道来说,会有更大的开销。

这是SpringMVC提供的一种技术,可以实现服务端向客户端实时推送数据.用法非常简单,只需要在Controller提供一个接口,创建并返回SseEmitter对象,发送数据可以在另一个接口调用其send方法发送数据。因此sse需要在spring mvc或更集成的框架中使用,在servlet中无法使用。

SpringMVC内置SseEmitter类内置了几个方法,可以令我相当方便地使用服务器推送事件(Server Sent Event)。

在使用前有几个关于sse的前端知识需要了解

  1. sse机制不同于传统的“请求-响应”模型,在前端必须使用新建的EventSource对象请求一个sse,
    然后监听此对象的message事件以接收后端推送的值。

  2. 在前端请求一个sse时,在后端未主动关闭和事件未超时前服务器都不会对此请求做出响应以实现“连接”的效果, 一旦后端响应这个请求(后端主动或“连接”自动超时)则代表“连接”结束;

  3. 前端一旦接收到响应则EventSource对象会立刻自动重新连接以保证连接的有效性。

    const source = new EventSource('http://localhost/sse');
    source.addEventListener('message', (message: any) => {
      console.log(message.data);
    });

使用SseEmitter的步骤

  1. 直接创建SseEmitter对象即可,创建时可设置一个超时时间(默认为30000毫秒),当达到此时间则立刻对请求响应(连接失效)。
    SseEmitter sse() throws IOException {

        SseEmitter event = new SseEmitter(10000L);

        // 添加一些额外配置
        event.send(
                SseEmitter.event()
                        .reconnectTime(1000L)
                        .id("123")
        );

        concurrentHashMap.put(1, event);
        return event;
    }
  1. 发送事件可以调用创建好的对象的send方法,发送的数据即为在前端的接收到的message事件中的data属性值

  2. 发送的数据默认被识别为字符串,如果发送Map则在前端需要用JSON对象解析才能得到json数据

        event.send(
                SseEmitter.event()
                        .data("值")
                        // 更改原来的“message”事件名称
                        .name("event")
        );
  1. 对于服务器端返回的响应,浏览器端需要在 JavaScript 中使用 EventSource对象来进行处理。EventSource 使用的是标准的事件监听器方式,只需要在对象上添加相应的事件处理方法即可。EventSource 提供了三个标准事件。
    在这里插入图片描述
var es = new EventSource('events');
es.onmessage = function(e) {
    console.log(e.data);
};
 
es.addEventListener('myevent', function(e) {
    console.log(e.data);
});

在指定 URL 创建出 EventSource 对象之后,可以通过 onmessage 和 addEventListener 方法来添加事件处理方法。当服务器端有新的事件产生,相应的事件处理方法会被调用。EventSource 对象的 onmessage 属性的作用类似于 addEventListener( ‘ message ’ ),不过 onmessage 属性只支持一个事件处理方法。

  1. SSE对象是服务端向客户端发送信息,因此需要周期性执行,需要借助线程功能。ScheduledExecutorService是基于ExecutorService的功能实现的延迟和周期执行任务的功能。每个任务以及每个任务的每个周期都会提交到线程池中由线程去执行,所以任务在不同周期内执行它的线程可能是不同的。ScheduledExecutorService接口的默认实现类是ScheduledThreadPoolExecutor。在周期执行的任务中,如果任务执行时间大于周期时间,则会以任务时间优先,等任务执行完毕后才会进入下一次周期。如下面所示:

/*
 数据实时推送sse数据推送
  */
//ScheduledExecutorService```是基于ExecutorService的功能实现的延迟和周期执行任务的功能
 ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

 @GetMapping(value = "/timeclick")
 public SseEmitter subscribeCC(){

     //获取数据库点击量数据
     //List<ClickTimes> clickTimes = null;

     SseEmitter sseEmitter = new SseEmitter(0L);
     executorService.scheduleWithFixedDelay(new Runnable() {
         @Override
         public void run() {
             try {
                 sseEmitter.send(opt1Mapper.select_all_times());
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }, 10,5, TimeUnit.SECONDS);  //每个任务10执行时间,每个5秒执行一次

     return sseEmitter;
 }

借助ScheduledExecutorServicescheduleWithFixedDelay方法周期性执行任务。

SSE案例

//接口是在spring boot的controller下实现的

 /*
    数据实时推送sse数据推送
     */
    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

    @GetMapping(value = "/timeclick")
    public SseEmitter subscribeCC(){

        //获取数据库点击量数据
        //List<ClickTimes> clickTimes = null;

        SseEmitter sseEmitter = new SseEmitter(0L);
        executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    sseEmitter.send(opt1Mapper.select_all_times());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, 10,5, TimeUnit.SECONDS);  //每个任务10执行时间,每个5秒执行一次

        return sseEmitter;
    }


 function initEventSource() {
        //请求地址,静态
        const url = "/timeclick"


        //接受实时推送数据的监听EventSource对象接收
        const dataSource = new EventSource(url);

        dataSource.onmessage= function (event){
            console.log("SSE--->收到数据")
            const vo = JSON.parse(event.data)
            //调用更新实时数据方法
            updateClickChart(vo)
        }

    }

在timeclick接口下,后台借助ScheduledExecutorService周期性返回数据,前端借助EventSource对象接收,再调用其他方法将接受的数据更新。所以sse技术适用用可视化方案。

在这里插入图片描述

上述的单车点击项目即sse的项目,放在资源里了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0到1学会开发前端脚手架

【课程简介】 在前端开发中经常会用到create-vue, create-react-app这类脚手架&#xff0c;它可以帮助我们快速生成一个配置化的项目&#xff0c;提高开发效率。现在很多大厂都有自己研发的脚手架&#xff0c;掌握脚手架的使用&#xff0c;并且自己能开发脚手架&#xff0c;能…

涵盖全场景构建方方面面!魅族2023-2025年产品矩阵曝光

在万物互联的时代大背景下&#xff0c;一众以智能手机闻名的科技厂商们开始了全场景概念上的推进构建&#xff0c;形如早前作为国产智能手机「领头羊」的老牌手机厂商魅族&#xff0c;就在近日公布了2023-2025年全场景多终端沉浸式的全方位产品矩阵。 从中可以看到&#xff0c…

解读最佳实践:倚天 710 ARM 芯片的 Python+AI 算力优化 | 龙蜥技术

编者按&#xff1a;在刚刚结束的 PyCon China 2022 大会上&#xff0c;龙蜥社区开发者朱宏林分享了主题为《ARM 芯片的 PythonAI 算力优化》的技术演讲。本次演讲&#xff0c;作者将向大家介绍他们在倚天 710 ARM 芯片上开展的 PythonAI 优化工作&#xff0c;以及在 ARM 云平台…

SCI 论文插图格式一般要求

插图是反映 SCI 文章品质的核心指标之一&#xff01;&#xff01;&#xff01; 图片格式要求:图片一般可以保存为TIFF、JPEG、EPS这三种常见格式,并存为独立文件。 二、图片色彩要求:一般要求为CMYK或RGB色彩。 1.尺寸符合杂志社的要求(宽度8.3~17.6厘米,高度一般不超过20厘米…

云服务下半场,企业增长的超级入口在哪?

随着数字技术的加速突破以及相关鼓励政策的出台&#xff0c;云服务市场正在进入爆发性增长阶段。 在市场、政策的双轮驱动下IT架构转型已是大势所向&#xff0c;而作为时代命题&#xff0c;传统厂商们如果还无法让市场看到转型的成果和信心&#xff0c;或将逐渐在市场端失去选…

专访实在智能孙林君:颠覆传统RPA的实在IPA模式,如何做到真正人人可用?

文/王吉伟 “RPA人人可用”这个愿景&#xff0c;在一线大厂提出后立即得到广大厂商的认同与推崇&#xff0c;之后它几乎成了所有厂商的“口头禅”。 如果RPA能够消除使用门槛实现真正人人可用&#xff0c;意味着每个组织都能通过引入RPA快速通过流程自动化快速实现增效降本。…

正大周二数据 产品介绍 主账户怎么拿到留4的条件?

《今日关注数据》 09:15 中国至12月20日一年期贷款市场报价利率 17:00 欧元区10月季调后经常帐(亿欧元) 21:30 加拿大10月零售销售月率 21:30 美国11月新屋开工总数年化(万户) 21:30 美国11月营建许可总数(万户) 美国银行在报告中指出&#xff0c;儘管美联准会 (Fed) 最近…

OLAP系统林林总总

大数据需求通常可分为三大类&#xff1a;离线数据统计&#xff0c;实时数据计算&#xff0c;即席查询。 离线数据统计&#xff0c;通常是T1出数&#xff0c;是最典型的数据仓库解决的问题。计算复杂性最高&#xff0c;所以是时间就不能要求太高&#xff0c;否则对资源的要求将…

【OpenFeign】【源码+图解】【三】FeignClient的配置信息

【OpenFeign】【源码图解】【二】注册OpenFeign接口的实例 目录4. FeignClient的配置信息4.1 FeignClientFactoryBean4.2 FeignClientFactoryBean.getObject()4.2.1 FeignContext4.2.2 Feign.Builder4.2.2.1 Bean的配置方式YMLFeignClient.configurationEnableFeignClients.def…

5.Node中的模块

目录 1 模块化 2 加载模块 3 模块作用域 4 module对象 4.1 初识module对象 4.2 module.exports 4.2.1 module.exports与require() 4.2.2 添加变量与方法 5 exports对象 5.1 初识exports对象 5.2 添加变量与方法 1 模块化 向我们之前引入的 fs,path,htt…

Baklib|为什么说企业需要重视客户体验?

数十年来&#xff0c;人们都在寻求使自己的品牌获得成功的秘密&#xff0c;企业和品牌也在不断地想着怎样在竞争中胜出&#xff0c;从而获得顾客的青睐。 有些公司已经竭尽全力争取顾客的信赖&#xff0c;并竭力争取更多的市场份额。 但是&#xff0c;即便那些著名的公司不会…

5G无线技术基础自学系列 | RF优化原理

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 RF优化主要是依据各种收集到的数据&…

C++ Primer 第五章 Statements

C Primer 第五章 Statements5.3. Conditional Statements5.3.2. The switch Statement5.4. Iterative Statements5.4.3. Range for Statement5.6. try Blocks and Exception Handling5.6.1. A throw Expression5.6.2. The try Block5.6.3. Standard Exceptions5.3. Conditional…

【传递窗】洁净区与非洁净室之间的传递

广州特耐苏净化设备有限公司详细介绍&#xff1a;传递窗分类及安装与使用方法 传递窗是一种洁净室的辅助设备&#xff0c;用于不同的洁净区之间&#xff0c;洁净区与非洁净区之间小物件的传递&#xff0c;具有气闸的作用&#xff0c;防止非洁净的空气进入洁净室。同时&#xf…

git cherry-pick 同步修改到另一个分支

我们在开发中有时会遇到&#xff0c;需要将另一个分支部分修改同步到当前分支。 如下图&#xff0c;想把 devA 分支中 commit E 和 F&#xff0c;同步到下面绿色的 devB 分支中。 这时候就可以使用 git cherry-pick 来完成这项任务。 &#xff08;cherry-pick 有筛选、精选的…

音视频开发——H265码流解析

概述 H.265技术的应用 编码技术主要运用于视频播放设备、软件应用以及拍摄、录制视频的设备。人们最熟悉的莫过于PPS网络视频播放器。在PC屏客户端产品上面&#xff0c;PPS已经于2013年初推出了基于H.265标准的高清视频&#xff0c;并命名“臻高清”为自己的高清品牌。同时 P…

极限一个月,突破Java高频面试题和核心技术,跳槽涨薪6K

前言 先说一下我的情况&#xff0c;双非本科毕业后就在几家小公司工作了3年&#xff0c;5月份的时候有去大厂面试过&#xff0c;结果都是凉凉&#xff0c;下面会跟大家分享一下我的经验以及建议。 有的朋友可能会认为学历是自己的问题&#xff0c;面试官连简历都不会看。其实…

Java Stream 编程

Java Stream 编程 教程&#xff1a;https://www.bilibili.com/video/BV1te411w722 1. 不可变集合 1.1 不可变集合应用场景 元数据&#xff0c;只允许查询的数据集合 1.2 创建不可变集合 List、Set、Map 接口中的静态方法 of() List<String> list List.of("张…

SpringCloud 2021版本教程:使用nacos作为注册中心和配置中心(2021版本+Boot2.7.6)

目录 前言 系列文章目录 一、什么是Nacos 1、nacos架构原理 2、nacos、eureka、zookeeper的区别 1、参考博客&#xff0c;写得很详细通俗易懂&#xff1a;一文说清eureka、zookepeer、nacos三者的关系 - 知乎 2、三者区别&#xff1a; 3、CAP原理&#xff1a; 二、项目…

MyBatis八股文

1、什么是MyBatis框架&#xff1f; MyBatis 是一款优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射&#xff0c;它内部封装了jdbc&#xff0c;不需要我们再写JDBC连接、使开发者只需要关注sql语句本身和业务&#xff0c;而不需要花费精力去处理加载驱动、…