【写在前面】写这篇文章的原因主要还是博主在工作的过程中遇到了一个困难,就是客户端开了两个一模一样的窗口(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加油鸭