小工具      在线工具  汉语词典  dos游戏  css  js  c++  java

实时通讯技术Ajax,WebSocket,SSE

数据通信与计算机网络,websocket,ajax,javascript 额外说明

收录于:17天前

实时通信技术是基于Web开发的一项重要技术。网站需要前端和后端通信,所以数据刷新时间就是获取信息的时间。为了准确、快速地获取信息,需要尽可能提高信息刷新效率。 。

常见的实时通信技术:

通讯方式 阿贾克斯 彗星 WebSockets 上证所
描述 短轮询是浏览器提交的表单查询 长轮询是指服务器收到请求后,如果有数据,则立即响应请求;如果没有数据,则等待一段时间,直到有数据,立即响应请求;如果时间到了还没有数据,就会响应http请求(定时刷新) WebSocket实现了连接后双方进行通信的功能。首先,客户端发出 WebSocket 请求,服务器通过 TCP 三向握手进行响应。一旦建立了该连接,它就保留在客户端和服务器之间,并且可以在两者之间直接传输数据。 sse场景下,客户端发起请求,并保持连接。当服务器有数据时,可以将数据返回给客户端。此返回可以是多个间隔。 sse是单通道,只有服务器端可以向客户端发送消息。
协议 http http 网络套接字 http

Ajax实现方案

Ajax实时通信实现起来比较方便。您可以通过浏览器DOM元素设置页面刷新时间来动态获取后端数据。

  • 添加 setinterval() 函数
<script language="javascript">
	setInterval(function(){
    
		window.location.reload();
	},3000);   //每隔3000毫秒刷新一次
</script>
  • Mata 添加内容元素并定期刷新它们
<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 做同样的事情。当您需要使用新数据部分更新 Web 应用程序时,SSE 可以做到这一点,而无需任何用户操作。 SSE是一种单向通道,服务器只能向客户端发送消息。如果客户端需要向服务器发送消息,则需要一个新的HTTP请求。这将比 WebSocket 的双工通道有更大的开销。

这是SpringMVC提供的一项技术,可以将数据从服务器实时推送到客户端。使用方法非常简单。只需要在Controller中提供一个接口,创建并返回SseEmitter对象,在另一个接口上调用其send方法发送数据即可。数据。因此sse需要在spring mvc或者集成度更高的框架中使用,不能在servlet中使用。

SpringMVC内置的SseEmitter类有几个内置方法可以让我非常方便的使用服务器推送事件(Server Sent Event)。

使用sse之前有几个关于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方法周期性执行任务。

上交所案例

//接口是在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项目,放置在资源中。

. . .

相关推荐

额外说明

AES CCM java代码

网络上很多AES加密方式,前不久玩着无聊向玩玩CCM加密解密,却发现百度和谷歌对于CCM的java加密解密代码太少了,今天刚刚琢磨出CCM的加密解密代码,不要问我细致的东西,俺只是研究前辈们的思路自己琢磨出的方式,废话不多说了,上代码。 import j

额外说明

设计模式的装修模式

前言: 装饰模式是动态的给一些对象添加一些职责,通过装饰模式比生成子类更灵活。就增加功能来说,装饰模式比生成子类更灵活。 一.装饰模式的简介 装饰模式是为已有的功能添加更多功能的一种方式。 应用场景: 系统需要新功能,向旧的类中增加代码,来装饰旧类的核心

额外说明

python牛顿环测量曲率半径

牛顿环实验,请从13直径环依次输入到4环,且牛顿环实验试验次数为偶次,奇次请舍去!!!Lambda = 0.0005893 该函数无图像!!! import numpy as np import matplotlib.pyplot as plt Lamb

额外说明

新年来到,特此制作一款烟花特效,预祝大家 虎虎生威,虎年大吉,生龙活虎

- 博客主页:https://xiaoy.blog.csdn.net - 本文由 呆呆敲代码的小Y 原创,首发于 CSDN- - 学习专栏推荐:Unity系统学习专栏 - 游戏制作专栏推荐:游戏制作专栏 - 欢迎点赞 - 收藏 ⭐留言 - 如有错误敬请指

额外说明

docker搭建pxc集群

前言 随着mysql存储的数据量越来越大,mysql查询单表时的响应速度也会随之变慢,尤其是当单节点承载的数据量超出一定的范围后,比如单表超过2000万之后,查询响应速度会下降的很快,因此,一方面可以考虑mysql集群,另一方面可以考虑读写分离,这两种方

额外说明

【从入门到起飞】JavaSE—Stream流

-专栏【JavaSE】 -喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 -音乐分享【如愿】 -欢迎并且感谢大家指出我的问题 文章目录 -Stream流的作用 -Stream流的使用步骤 -获取Stream流 ⭐单列集合获取Stream流 ⭐双列集合获取S

额外说明

echarts折线图显示每个折点的数值

解决办法 在series里添加进去 series.itemStyle.normal.label的值为true itemStyle:{ normal:{ label:{ show:true //在每个上

额外说明

Java基础 : 更改器方法、访问器方法和构造器方法

首先我们从字面可以理解一下 更改器就是会改变原来的数据; 访问器只是会查看数据; 而构造器就是创建。 下面我们通过代码示例理解一下: 我们创建一个 Computer类,定义了两个属性cpuname和db。 由于这两个属性用private修饰,在其他类是不

额外说明

sql语句计算百分百

sql统计 SELECT b.circles_name,b.num, a.total,CONCAT(CAST(round((b.num*1.0/a.total)*100,2) AS CHAR),'%') as rate FROM (SELECT COU

ads via 小工具