服务器推送技术

服务器推送技术需求与背景之前所有的请求都是浏览器发起,浏览器本身没有接受请求的能力。所以一些特殊需求都是用ajax轮询的方式来实现的。比如:股价展示页面实时的获取股价更新赛事的文字直播,实时更新赛况通过页

大家好,又见面了,我是你们的朋友全栈君。

需求与背景

之前所有的请求都是浏览器发起,浏览器本身没有接受请求的能力。所以一些特殊需求都是用ajax轮询的方式来实现的。

比如:

  • 股价展示页面实时的获取股价更新
  • 赛事的文字直播,实时更新赛况
  • 通过页面启动一个任务,前端想知道任务后台的实时运行状态

HTML5推广之后,服务端主动推送数据,浏览器接受数据的方式来解决

全双工通信(full-duplex communication)—— WebSocket

全双工就是双向通信,WebSocket就是移动电话(可以随时发送信息与接受信息,就是全双工)
<span role="heading" aria-level="2">服务器推送技术

本质上是一个额外的TCP连接,建立和关闭时握手使用HTTP协议,其他数据传输不使用HTTP协议

  • HTTP协议适用于复杂双向实时数据通讯场景
  • 在Web网页上的客服、聊天室一般使用WebSocket开发

服务端主动推送: SSE(Server Send Event)

HTML5新标准,用来从服务端实时推送数据到浏览器端,直接建立在当前HTTP连接上
本质上是保持一个HTTP长连接、轻量协议。

=》 客户端发送一个请求 到服务端,服务端保持这个请求连接知道一个新的消息准备好,将消息返回到客户端。除非主动关闭,否则一直保持连接。

  • 建立连接
  • 服务端 -> 浏览器(连接保持)
  • 关闭连接

SSE一大特色: 重复利用1个连接来接受服务器发送的消息(event),从而避免不断轮询请求建立连接,造成服务资源紧张。

是否基于新协议 是否双向通信 是否支持跨域
SSE 否(Http) 否(单向) 否(Firefox 支持跨域)
WebSocket 是(ws)

模拟网络支付场景

Eg: 淘宝买一个产品之后进行扫码支付,结合SSE实现这个过程

<span role="heading" aria-level="2">服务器推送技术

关键代码

<span role="heading" aria-level="2">服务器推送技术

浏览器前端实现

对于服务器端像浏览器发送的数据,浏览器端需要在JavaScript中使用EventSource对象来进行处理。EventSource使用的是标准的事件监听器方式,只需要在对象上添加相应事件处理方法。

事件名称 事件触发说明 事件处理方法
open 当服务器向浏览器第一次发送数据时产生 onopen
message 当收到服务器发送的消息时产生 onmessage
error 当出现异常时产生 onerror

兼容HTTPS协议

  • WebSocket的ws协议是基于HTTP协议实现的。
  • WebSocket的wss协议是基于HTTPS协议实现的。

一旦你的项目里面使用了https协议,你的websocket就要使用wss协议才可以。怎么让Spring Boot项目支持WSS协议?
<span role="heading" aria-level="2">服务器推送技术

WebSocket编程基础

连接的建立
  • 前端JS向后端发送WSS连接建立请求
socket = new WebSocket("wss://localhost:8888/ws/asset");
  • SpringBoot服务端WebSocket服务接收类定义如下:
@Component
@Slf4j
@ServerEndpoint(value = "/ws/asset")
public class WebSocketServer{

}
  • 全双工数据交互
    前端后端都有
    onopen事件监听,处理连接建立事件
    onmessage事件监听,处理对方发过来的消息数据
    onclose事件监听,处理连接关闭
    onerror事件监听,处理交互过程中的异常

<span role="heading" aria-level="2">服务器推送技术

<span role="heading" aria-level="2">服务器推送技术

<span role="heading" aria-level="2">服务器推送技术

示例: websocket实现聊天软件

WebSocketServer本节内容的核心代码,websocket服务端代码

@ServerEndpoint(value = “/ws/asset”)表示websocket的接口服务地址
@OnOpen注解的方法,为连接建立成功时调用的方法
@OnClose注解的方法,为连接关闭调用的方法
@OnMessage注解的方法,为收到客户端消息后调用的方法
@OnError注解的方法,为出现异常时调用的方法

  • 服务端代码
@Component
@Slf4j
@ServerEndpoint(value = "/ws/asset")
public class WebSocketServer {  

    //用来统计连接客户端的数量
    private static final AtomicInteger OnlineCount = new AtomicInteger(0);
    // concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。  
    private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<>();
    
    /** 
     * 连接建立成功调用的方法 
     */  
    @OnOpen
    public void onOpen(Session session) throws IOException {
        SessionSet.add(session);   
        int cnt = OnlineCount.incrementAndGet(); // 在线数加1  
        log.info("有连接加入,当前连接数为:{}", cnt);
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        log.info("来自客户端的消息:{}",message);
        sendMessage(session, "Echo消息内容:"+message);
        // broadCastInfo(message); 群发消息
    }


    /** 
     * 连接关闭调用的方法 
     */  
    @OnClose
    public void onClose(Session session) {  
        SessionSet.remove(session);  
        int cnt = OnlineCount.decrementAndGet();  
        log.info("有连接关闭,当前连接数为:{}", cnt);  
    }  

    /** 
     * 出现错误
     */  
    @OnError
    public void onError(Session session, Throwable error) {  
        log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId());
    }  
  
    /** 
     * 发送消息,实践表明,每次浏览器刷新,session会发生变化。 
     * @param session  session
     * @param message  消息
     */  
    private static void sendMessage(Session session, String message) throws IOException {

        session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));

    }  
  
    /** 
     * 群发消息 
     * @param message  消息
     */  
    public static void broadCastInfo(String message) throws IOException {
        for (Session session : SessionSet) {  
            if(session.isOpen()){  
                sendMessage(session, message);
            }  
        }  
    }
      
} 

客户端代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>websocket测试</title>
    <style type="text/css">
        h3,h4{
            text-align:center;
        }
    </style>
</head>
<body>

<h3>请输入要发送给服务器端的消息:</h3><br/>

<label for="text">输入发送信息</label><input id="text" type="text" />
<button onclick="sendToServer()">发送服务器消息</button>
<button onclick="closeWebSocket()">关闭连接</button>
<br>
信息:
<span id="message">

</span>
<script type="text/javascript">
    var socket;
    if (typeof (WebSocket) == "undefined") {
        console.log("遗憾:您的浏览器不支持WebSocket");
    } else {
        socket = new WebSocket("wss://localhost:8888/ws/asset");
        //连接打开事件
        socket.onopen = function() {
            console.log("Socket已打开");
        };
        //收到消息事件
        socket.onmessage = function(msg) {
            document.getElementById('message').innerHTML += msg.data + '<br/>';
        };
        //连接关闭事件
        socket.onclose = function() {
            console.log("Socket已关闭");
        };
        //发生了错误事件
        socket.onerror = function() {
            alert("Socket发生了错误");
        };

        //窗口关闭时,关闭连接
        window.unload=function() {
            socket.close();
        };
    }

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

    //发送消息给服务器
    function sendToServer(){
        var message = document.getElementById('text').value;
        socket.send(message);
    }
</script>

</body>
</html>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/155724.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • vim鼠标模式打开与关闭

    vim鼠标模式打开与关闭

  • 程序已在运行,只能运行一个实例_最常用解决冲突的办法

    程序已在运行,只能运行一个实例_最常用解决冲突的办法//开发中常用的只允许一个程序运行的办法//程序以单例模式运行常用办法,创建一个互斥量//由于互斥量只允许一个进程或者线程占用会创建失败,利用这个特性可以做到单例运行改程序#include"stdafx.h"#include&lt;windows.h&gt;#include&lt;stdio.h&gt;int_tmain(intargc,_TCHAR*argv[]…

  • oracle数据库用户密码过期后怎么修改_oracle数据库密码过期怎么修改

    oracle数据库用户密码过期后怎么修改_oracle数据库密码过期怎么修改问题现象:今天在更改数据库数据的时候,程序报错了,如下:ORA-28001:thepasswordhasexpired问题分析:很显然,报错原因就是:密码已过期!所以现在需要做的事情只有两件:1.修改密码的过期时间2.修改/重置密码这里分析一下为什么要这样做:1.修改密码的过期时间:这是因为OracleDatabase11g版本的Oracle数据库有一项默认配置,就是密码过期时间默认为180天(6个月左右);通过sysdba身份可以登录..

  • EXCEL countif函数多条件

    在MSEXCEL中,countif如果要满足多个条件怎么办呢?1、答案就是:使用条件集合{}和sum函数。即在countif()第二个参数中使用条件集合{},然后用sum()函数求满足这些条件的

    2021年12月26日
  • pdf加密文件怎么解密_打开天正加载自定义文件失败

    pdf加密文件怎么解密_打开天正加载自定义文件失败0.前言在学习Java的类加载器的时候,我们都会看到类加载器的体系结构上图红色框住的就是jvm提供的三个类加载器,而除了这三个外还有一个自定义类加载器。我们学习一门技术,一定要先知道为什么要学习这门技术,这门技术有什么用,比如说自定义类加载器,我们为什么要自定义类加载器。加密:加密class文件解密:用自定义的类加载器去解密并加载加密过的class文件

  • 圆柱表面积公式计算器_根据体重体表面积计算公式

    圆柱表面积公式计算器_根据体重体表面积计算公式【www.shanpow.com–在线新华字典】体积计算器在线计算篇(一):常用体积计算公式多面体的体积和表面积:有立方体计算公式、长方体∧棱柱∨计算公式、三棱柱计算公式、棱锥计算公式、棱台计算公式、圆柱和空心圆柱∧管∨计算公式、斜线直圆柱计算公式、直圆锥计算公式、圆台计算公式、球计算公式、球扇形∧球楔∨计算公式、球缺计算公式、圆环体∧胎∨计算公式、球带体计算公式、桶形计算公式、椭球体计算公式、…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号