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

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

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

效果查看

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

在这里插入图片描述

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

在这里插入图片描述

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加油鸭

猜你喜欢

转载自blog.csdn.net/hdp134793/article/details/129858869
今日推荐