【websocket消息推送】前端+后端实现websocket消息推送的整个生命周期(附源码详解)

写在前面】写这篇文章的原因主要还是博主在工作的过程中遇到了一个困难,就是客户端开了两个一模一样的窗口(A和B),然后A窗口触发一个请求,请求后是推送到前端的,但是推送的消息只推给了B,而A没有拿到推送的数据,导致A页面一直在等待推送的结果,从而页面出现长时间等待中,又不可能每次都和用户说只能开一个窗口吧。
涉及知识点:前端+后端如何实现websocket消息推送,websocket推送,消息推送,前后端监听,事件推送,websocket推送消息,websocket使用。

版权声明:由于好多网站爬取,本文原创于CSDN博主《拄杖盲学轻声码》

消息推送目录

  • 效果查看
    • 单页面测试效果(必须得登录才能推送)
    • 多页面测试效果(相同页面不会推送错乱)
  • 1、websocket推送原理
    • 1.1 Web的携带信息
    • 1.2 Web的匹配校验
  • 2、websocket推送实现(java后端+jsp前端)
    • 2.1 Web前端如何实现(jsp为例)
      • A、设置窗口唯一标识
      • B、创建websocket函数【web核心】
        • <1> 封装参数;用户信息,客户端IP,客户端窗口标识
        • <2> 设置websocket《new WebSocket()》
        • <3> 状态判断websocket.readyState
        • <4> 关闭推送websocket.close()
        • <5> 连接异常方法websocket.onerror
        • <6> 连接成功回调函数websocket.onopen, 成功后将参数发送给后
        • <7> 接收推送消息函数websocket.onmessage
    • 2.2 Java后端如何实现
      • A. pom引入websocket依赖
      • B.搭建websocket基础文件
      • C.调用方法(后端核心实现)
        • <1> . 登录时用户信息存储
        • <2>定义接口实现
      • D.测试效果
  • 3、如何解决多个相同页面推送问题
    • 3.1 生成UUID标识
    • 3.2 封装并传递UUID
  • 4、文件分享(websocket基础java文件)
    • 4.1 百度网盘
    • 4.2 123云盘
    • 4.3 彩蛋皇榜

效果查看

单页面测试效果(必须得登录才能推送)

在这里插入图片描述

多页面测试效果(相同页面不会推送错乱)

在这里插入图片描述

1、websocket推送原理

核心原理就是通过web信息,再在后端做匹配校验,oK则消息推往web端。

1.1 Web的携带信息

A、当前登录用户名,当前客户端IP
B、当前客户端窗口标识UUID,

1.2 Web的匹配校验

判断是否为当前用户当前客户端
判断是否为在线用户且当前浏览器窗口

2、websocket推送实现(java后端+jsp前端)

2.1 Web前端如何实现(jsp为例)

A、设置窗口唯一标识

设置当前页面唯一标识,主要用于精准推送,可以自己随机生成一个UUID,然后放到sessionStorage里面,如下所示:
// 初始化推送唯一id, 为避免嵌入时没有值时也添加了随机值

if(!$.isNotNull(sessionStorage.getItem("websocket_user_uuid"))){
   sessionStorage.setItem("websocket_user_uuid", UUID());
}

B、创建websocket函数【web核心】

sendWebSocketMsg()方法函数,用于建立前后端连接后,异常处理,关闭处理,接收推送消息后的一些处理。
核心知识点梳理:

<1> 封装参数;用户信息,客户端IP,客户端窗口标识

定义变量:var tstr = userName +“,”+localIP + “,” + userId + “,” +UUID;

<2> 设置websocket《new WebSocket()》

<3> 状态判断websocket.readyState

<4> 关闭推送websocket.close()

<5> 连接异常方法websocket.onerror

<6> 连接成功回调函数websocket.onopen, 成功后将参数发送给后

websocket.send(tstr)

<7> 接收推送消息函数websocket.onmessage

实现源码如下所示:

function sendWebSocketMsg(){
//判断当前浏览器是否支持WebSocket
var tstr = userName +","+localIP + "," + userId + "," + sessionStorage.getItem("websocket_user_uuid");
if ('WebSocket' in window && !$.isNotNull(websocket)) {
   console.log("websocket重新创建!");
   //此处参数只允许字符串,不支持json
   websocket = new WebSocket(getRootPath().replace("http","ws")+"/websocket/"+tstr);
}
// 已经连接
if(websocket.readyState == 1){
   console.log("webSocket此处已经建立连接!")
   return;
}else{
   console.log("webSocket连接成功!");
}
//连接发生错误的回调方法
websocket.onerror = function () {
   websocketClose();
};

//连接成功建立的回调方法
websocket.onopen = function () {
   //setInterval(function(){
   websocket.send(tstr);
   //}, 10 * 1000);
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
   try {
	    //接收到websocket推送消息后此处
		
	}catch (e) {
	    //异常处理
	}
}
//连接关闭的回调方法
websocket.onclose = function () {
   websocketClose();
}
}
//关闭连接函数
function websocketClose(){
   try {
      websocket.close();
   } catch (e) {
   }
   websocket = null;
}

2.2 Java后端如何实现

A. pom引入websocket依赖

然后进行下载更新自己的本地maven,主要目的就是下载java依赖包,将下面的代码复制到pom.xml文件中后,记得下载maven.

<!-- websocket包 -->
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0</version>
    <scope>provided</scope>
</dependency>

B.搭建websocket基础文件

在自己的utils文件下创建一个websocket文件,用于存放基础po和方法文件,针对里面的一些依赖报错和环境路径有关,配置好自己的目录路径(源码文件文尾有分享),如下所示:
在这里插入图片描述

C.调用方法(后端核心实现)

<1> . 登录时用户信息存储

首先登录时候,我们将用户信息放在session里面,方便后面推送的比对,不然也不知道哪个用户啊。如下:

public User search(HttpServletRequest request,User po) {
//        User userInfo = (User) request.getSession().getAttribute("user");
        Integer id = po.getId();
        User user = unobserve.selectByPrimaryKey(id);
        //获取客户端IP地址
        String reqIpStr = IpUtils.getIpAddr(request);
        user.setIp(reqIpStr);
        //将用户信息存在session里面,其实这个地方我们相当于模拟登陆了
        HttpSession session = request.getSession();
        session.setAttribute("user", user);
        return user;
    }

<2>定义接口实现

我是采用一个接口去调用公共方法sendSocketMsg做推送的,方便多个接口应用

@ResponseBody
@RequestMapping(value = "/doSendSocketMsg")
public void doSendSocketMsg(HttpServletRequest request, @RequestHeader(name="websocket_user_uuid") String useruuid) {
    //推送触发函数
    User userInfo = (User) request.getSession().getAttribute("user");
    JSONObject jsonObj = new JSONObject();
    String ip = IpUtils.getIpAddr(request);
    String userInfoStr = userInfo.getName()+":"+ip+":"+useruuid;
    jsonObj.put("userInfo",userInfoStr);
    String result = "我要推送的消息就是,黄大大好不要脸!";
    sendSocketMsg(result, jsonObj);
}
//发送推送的消息
public void sendSocketMsg(String result, JSONObject userSocInfo) {
    logger.info("=======================================================!");
    logger.info("返回推送结果!");
    logger.info("=======================================================!");
    for (WebSocketForWeb item : WebSocketForWeb.getWebSocketSet()) {
        String[] userInfos = userSocInfo.get("userInfo").toString().split(":");
        if (userInfos[0].equals(item.userName) && userInfos[1].equals(item.ip) && userInfos[2].equals(item.userUUID)) {
            try {
                JSONObject jsonObj = new JSONObject();
                jsonObj.put("sendmsg", result);
                if (true) {
                    jsonObj.put("socket_type", WebSocketForWeb.INDEX_TEST_EVENT);
                }
                item.sendMessage(jsonObj.toJSONString());
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
}

D.测试效果

首先通过单页面点击触发调用推送接口,我是做了一个模拟登陆的,登陆成功的话后端会将用户数据塞到session,然后通过前端websocket传递的用户数据和后端session数据进行匹对,如果OK,则进行推送,我在前端触发后是将推送的消息直接放在dom里面展示,如下所示:
在这里插入图片描述

只有点击模拟登陆后才能在点击推送后触发websocket推送消息。

版权声明:由于好多网站爬取,本文原创于CSDN博主《拄杖盲学轻声码》

3、如何解决多个相同页面推送问题

3.1 生成UUID标识

读完第二节我困惑的问题已经解决了,因为在上面第二章节的过程中我设置了一个窗口标识字段,其中在java会做窗口标识字段的比对,如果是一样的则推送,否则就不推送。其实这个窗口标识我是用了一个随机32位数字符(数字+字母混合)代替的
随机数生成函数如下:

function UUID() {
    var len = 32;//32长度
    var radix = 16;//16进制
    var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
    var uuid = [], i;
    radix = radix || chars.length;
    if (len) {
        for (i = 0; i < len; i++) {
            uuid[i] = chars[0 | getRandom() * radix];
        }
    } else {
        var r;
        uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
        uuid[14] = '4';
        for (i = 0; i < 36; i++) {
            if (!uuid[i]) {
                r = 0 | getRandom() * 16;
                uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
            }
        }
    }
    return uuid.join('');
}

3.2 封装并传递UUID

我们看控制台不难发现,前提是我们在websocket消息推送的时候,也会将这个uuid封装在请求头里面,如下所示web推送消息接口函数:

//前端推送函数
function getSocketMessage(){
    debugger;
    var portUrl = getContextPath() + "/userCon/doSendSocketMsg";
    $.ajax({
        url: portUrl,
        type: 'POST',
        beforeSend: function (xhr, infos) {
            //在请求头里面塞uuid
            if (!$.isNotNull(sessionStorage.getItem("websocket_user_uuid"))) {
                sessionStorage.setItem("websocket_user_uuid", UUID());
            }
            // 增加websocket_user_uuid请求头,用于推送到某个标签页
            xhr.setRequestHeader("websocket_user_uuid", sessionStorage.getItem("websocket_user_uuid"));
        },
        success: function (result) {
            debugger;
        },

    });
}

我是放在sessionStorage里面,每次登录请求后都会有初始化的新uuid.生成的串如下控制台所示:

4、文件分享(websocket基础java文件)

4.1 百度网盘

链接:https://pan.baidu.com/s/1K_tAT-bdgklo6lGdw88H6w
提取码:hdd6

4.2 123云盘

提取地址:https://www.123pan.com/s/ZxkUVv-B2J4.html
提取码:hdd6

4.3 彩蛋皇榜

倾心打造佳作,愿解君之惑,如若有幸,盼君上榜助阵,特此敬谢!
皇榜入口点击此处

版权声明:由于好多网站爬取,本文原创于CSDN博主《拄杖盲学轻声码》
如有困惑欢迎前来留言讨论,一起进步哈!!!2023加油鸭

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++笔试强训】第三天

选择题 解析&#xff1a;字符数组里面的最后一个字符是0&#xff0c;说明里面本身就是一个字符串——"123456789"&#xff0c;数组名表示数组首元素的地址&#xff0c;那么p a i指向的就是字符数组中元素9&#xff0c;那么p - 3就是指向元素6的地址&#xff0c;%s打…

在VScode中配置Python开发环境----需要注意的一个点:settings.json

在VScode中配置Python开发环境&#xff08;可以参考这个博主的方法&#xff09;&#xff1a; http://t.csdn.cn/L1jux 1、安装python 官网下载地址&#xff1a;https://www.python.org/ftp/python/3.8.0/python-3.8.0-amd64.exe 双击打开.exe文件 勾选 Add Python 3.8 to Pat…

【计算机视觉 | 目标检测】DETR风格的目标检测框架解读

文章目录一、前言二、理解2.1 DETR的理解2.2 DETR的细致理解2.2.1 Backbone2.2.2 Transformer encoder2.2.3 Transformer decoder2.2.4 Prediction feed-forward networks (FFNs)2.2.5 Auxiliary decoding losses2.3 更具体的结构2.4 编码器的原理和作用2.5 解码器的原理和作用…

刚刚,Frontiers in Psychology 取消on hold状态,但这本期刊仍在评估中

3月28日时&#xff0c;Frontiers in Psychology仍处于on hold状态。 就在刚刚&#xff01;小编查询Frontiers in Psychology时&#xff0c;发现Master Journal List中&#xff0c;期刊Frontiers in Psychology的on hold标识没有了&#xff0c;这表示期刊目前正被SSCI数据库收录…

独立部署基于apiKey或accessToken的GPT聊天工具

最近chat-GPT的强大功能让人新潮澎湃,大家都在讨论,都想尝试一下。。。 奈何用不了!自己整整,内附具体步骤,如何用手机验证码注册,如何自己搭一个前端,nodejs后端,可以访问自己的GTP。 先上图: 自己搭的: 官网: 步骤一、用个代理 因为没这个无法访问GPT官网 忍…

类与对象,对象在内存的存在形式,java方法

类是抽象的&#xff0c;概念的&#xff0c;代表一类事物&#xff0c;比如人类&#xff0c;猫类..即它是数据类型对象是具体的实际的&#xff0c;代表一个具体事物&#xff0c;即实例。类是对象的模板&#xff0c;对象是类的一个个体&#xff0c;对应一个实例 public class Targ…

Jenkins入门

Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具 持续集成&#xff08;CI&#xff09;是在源代码变更后自动检测、拉取、构建和&#xff08;在大多数情况下&#xff09;进行单元测试的过程 可以简单将jenkins理解为一个代码部署工具。 在没有持续部署工具之前&#x…

【Redis进阶】Redis数据结构

文章目录1. 前言2. SDS2. 链表3. 压缩链表4. 哈希表5. 整数集合6. 跳表7. quicklist8. listpack1. 前言 Redis常用的数据结构为String&#xff0c;List&#xff0c;Hash&#xff0c;Set&#xff0c;Sorted Set。但这只是我们在用的时候键值对的表现形式&#xff0c;他们底层真…

《程序员面试金典(第6版)》面试题 08.05. 递归乘法

题目描述 递归乘法。 写一个递归函数&#xff0c;不使用 * 运算符&#xff0c; 实现两个正整数的相乘。可以使用加号、减号、位移&#xff0c;但要吝啬一些。 示例1: 输入&#xff1a;A 1, B 10 输出&#xff1a;10 示例2: 输入&#xff1a;A 3, B 4 输出&#xff1a;…

vue3使用useMouseInElement实现图片局部放大预览效果

1、首先要安装vueuse/core npm i vueuse/core2、实现过程如下&#xff1a; <template><div class"goods-image"><!-- 大图 --><div v-show"show" class"large" :style"[{backgroundImage:url(${images[currIndex]})…

Cursor,程序员的 AI 代码编辑助手

相信大家都或多或少地听说过、了解过 chatGPT &#xff0c;半个月前发布的 GPT-4 &#xff0c;可谓是 AI 赛道上的一个王炸 那么今天咸鱼给大家分享一个开源的 AI 代码编辑器——Cursor&#xff0c;让各位程序员在编程之路上一骑绝尘 &#x1f603; 介绍 Cursor 是一个人工智…

深度学习应用技巧4-模型融合:投票法、加权平均法、集成模型法

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下&#xff0c;深度学习中的模型融合。它是将多个深度学习模型或其预测结果结合起来&#xff0c;以提高模型整体性能的一种技术。 深度学习中的模型融合技术&#xff0c;也叫做集成学习&#xff0c;是指同时使用多个…

209. 长度最小的子数组

209. 长度最小的子数组 力扣题目链接(opens new window) 给定一个含有 n 个正整数的数组和一个正整数 s &#xff0c;找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组&#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0。 示例&#xff1a; 输入…

Java实现查找文件

1 问题 如何利用java来查找文件&#xff1f; 2 方法 2.1定义一个查找类&#xff0c;设置两个参数&#xff08;查找的目录和文件后缀名&#xff09;&#xff0c;然后判断文件夹是否为空 2.2 判断是否是文件夹&#xff0c;如果是文件夹则将里面的文件放入数组进行遍历&#xff08…

【Python零基础学习入门篇①】——基本语法与变量

⬇️⬇️⬇️⬇️⬇️⬇️ ⭐⭐⭐Hello&#xff0c;大家好呀我是陈童学&#xff0c;一个普通大一在校生&#xff0c;请大家多多关照呀嘿嘿&#x1f601;&#x1f60a;&#x1f618; &#x1f31f;&#x1f31f;&#x1f31f;技术这条路固然很艰辛&#xff0c;但既已选择&#x…

Redis 事务相关操作

Redis 作为一个非关系型内存数据库&#xff0c;也有事务定义 1. 事务的定义-ACID特性 A表示原子性&#xff1a;即事务是一个不可分割的实体&#xff0c;事务中的操作要么都完成&#xff0c;要么都不完成 C表示一致性&#xff1a;即事务前后数据完整性必须一致&#xff0c;假…

基于springboot实现数码论坛系统设计与实现演示【附项目源码+论文说明】

基于springboot实现数码论坛系统设计与实现演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

Jieba分词的准确率提升:使用paddle模式进行分词(使用百度飞桨深度学习模型进行分词)

1 Paddle模式简介 jieba中的paddle模式是指使用飞桨&#xff08;PaddlePaddle&#xff09;深度学习框架加速分词的一种模式。相对于传统的分词算法&#xff0c;paddle模式采用了深度学习模型&#xff0c;可以获得更高的分词准确度和更快的分词速度。 paddle模式是基于卷积神经…

数据分析之Pandas(2)

3.Pandas 文章目录3.Pandas3.3 Pandas进阶3.3.1 数据重塑和轴向旋转&#xff08;1&#xff09;层次化索引Series的层次化索引DataFrame的层次化索引层次化——电影数据示列&#xff08;2&#xff09;数据旋转3.3.2 数据分组、分组运算3.3.3 离散化处理3.3.4 合并数据集&#xf…

使用langchain打造自己的大型语言模型(LLMs)

我们知道Openai的聊天机器人可以回答用户提出的绝大多数问题,它几乎无所不知&#xff0c;无所不能&#xff0c;但是由于有机器人所学习到的是截止到2021年9月以前的知识&#xff0c;所以当用户询问机器人关于2021年9月以后发送的事情时&#xff0c;它无法给出正确的答案&#x…