Spring Boot 集成 WebSocket,轻松实现信息推送!
上一篇:深夜看了张一鸣的微博,让我越想越后怕
在一次项目开发中,使用到了Netty 网络应用框架,以及 MQTT 进行消息数据的收发,这其中需要后台来将获取到的消息主动推送给前端,于是就使用到了MQTT,特此记录一下。
一、什么是websocket?
WebSocket 协议是基于 TCP 的一种新的网络协议。
它实现了客户端与服务器之间的全双工通信,学过计算机网络都知道,既然是全双工,就说明了服务器可以主动发送信息给客户端。
这与我们的推送技术或者是多人在线聊天的功能不谋而合。
为什么不使用 HTTP 协议呢?
这是因为HTTP是单工通信,通信只能由客户端发起,客户端请求一下,服务器处理一下,这就太麻烦了。
于是 websocket 应运而生。
下面我们就直接开始使用 Spring Boot 开始整合。以下案例都在我自己的电脑上测试成功,你可以根据自己的功能进行修改即可。
我的项目结构如下:
二、使用步骤
1.添加依赖
Maven 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.启用Springboot对WebSocket的支持
启用 WebSocket 的支持也是很简单,几句代码搞定。Spring Boot 最新教程推荐看这个:https://github.com/javastacks/spring-boot-best-practice
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/*** @ Auther: 马超伟* @ Date: 2020/06/16/14:35* @ Description: 开启WebSocket支持*/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
3.核心配置:WebSocketServer
因为 Web Socket 是类似客户端服务端的形式(采用 ws 协议),那么这里的 WebSocketServer 其实就相当于一个 ws 协议的 Controller。
@ServerEndpoint 注解这是一个类层次的注解,它的功能主要是将目前的类定义成一个 websocket 服务器端。注解的值将被用于监听用户连接的终端访问 URL 地址,客户端可以通过这个 URL 来连接到 WebSocket 服务器端
再新建一个 ConcurrentHashMap webSocketMap 用于接收当前 userId 的 WebSocket,方便传递之间对 userId 进行推送消息。
下面是具体业务代码:
package cc.mrbird.febs.external.webScoket;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;/*** Created with IntelliJ IDEA.* @ Auther: 马超伟* @ Date: 2020/06/16/14:35* @ Description:* @ ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端*/
@Component
@Slf4j
@Service
@ServerEndpoint("/api/websocket/{sid}")
public class WebSocketServer {//当前在线连接数private static int onlineCount = 0;//存放每个客户端对应的MyWebSocket对象private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();private Session session;//接收sidprivate String sid = "";/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {this.session = session;webSocketSet.add(this); //加入set中this.sid = sid;addOnlineCount(); //在线数加1try {sendMessage("conn_success");log.info("有新窗口开始监听:" + sid + ",当前在线人数为:" + getOnlineCount());} catch (IOException e) {log.error("websocket IO Exception");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {webSocketSet.remove(this); //从set中删除subOnlineCount(); //在线数减1log.info("释放的sid为:"+sid);log.info("有一连接关闭!当前在线人数为" + getOnlineCount());}/*** 收到客户端消息后调用的方法* @ Param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {log.info("收到来自窗口" + sid + "的信息:" + message);//群发消息for (WebSocketServer item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();}}}/*** @ Param session* @ Param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("发生错误");error.printStackTrace();}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}/*** 群发自定义消息*/public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {log.info("推送消息到窗口" + sid + ",推送内容:" + message);for (WebSocketServer item : webSocketSet) {try {//为null则全部推送if (sid == null) {
// item.sendMessage(message);} else if (item.sid.equals(sid)) {item.sendMessage(message);}} catch (IOException e) {continue;}}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount--;}public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {return webSocketSet;}
}
4.测试Controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** Created with IntelliJ IDEA.** @ Auther: 马超伟* @ Date: 2020/06/16/14:38* @ Description:*/
@Controller("web_Scoket_system")
@RequestMapping("/api/socket")
public class SystemController {//页面请求@GetMapping("/index/{userId}")public ModelAndView socket(@PathVariable String userId) {ModelAndView mav = new ModelAndView("/socket1");mav.addObject("userId", userId);return mav;}//推送数据接口@ResponseBody@RequestMapping("/socket/push/{cid}")public Map pushToWeb(@PathVariable String cid, String message) {Map<String,Object> result = new HashMap<>();try {WebSocketServer.sendInfo(message, cid);result.put("code", cid);result.put("msg", message);} catch (IOException e) {e.printStackTrace();}return result;}
}
5.测试页面index.html
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Java 后端 WebSocket 的 Tomcat 实现</title><script type="text/javascript" src="js/jquery.min.js"></script></head><body><div id="main" style="width: 1200px;height:800px;"></div>Welcome<br/><input id="text" type="text" /><button onclick="send()">发送消息</button><hr/><button onclick="closeWebSocket()">关闭WebSocket连接</button><hr/><div id="message"></div></body><script type="text/javascript">var websocket = null;//判断当前浏览器是否支持WebSocketif('WebSocket' in window) {websocket = new WebSocket("ws://192.168.100.196:8082/api/websocket/100");} else {alert('当前浏览器 Not support websocket')}//连接发生错误回调方法websocket.onerror = function() {setMessageInnerHTML("WebSocket连接发生错误");};//连接成功建立回调方法websocket.onopen = function() {setMessageInnerHTML("WebSocket连接成功");}var U01data, Uidata, Usdata//接收消息回调方法websocket.onmessage = function(event) {console.log(event);setMessageInnerHTML(event);setechart()}//连接关闭回调方法websocket.onclose = function() {setMessageInnerHTML("WebSocket连接关闭");}//监听窗口关闭事件window.onbeforeunload = function() {closeWebSocket();}//将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}//关闭WebSocket连接function closeWebSocket() {websocket.close();}//发送消息function send() {var message = document.getElementById('text').value;websocket.send('{"msg":"' + message + '"}');setMessageInnerHTML(message + "
");}</script></html>
6.结果展示
后台:
如果有连接请求
前台显示:
总结
这中间我遇到一个问题,就是说 WebSocket 启动的时候优先于 spring 容器,从而导致在 WebSocketServer 中调用业务Service会报空指针异常。
所以需要在 WebSocketServer 中将所需要用到的 service 给静态初始化一下:
如图所示:
还需要做如下配置:
原文:blog.csdn.net/MacWx/article/details/111319558
感谢您的阅读,也欢迎您发表关于这篇文章的任何建议,关注我,技术不迷茫!小编到你上高速。
· END ·
最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。
正文结束
推荐阅读 ↓↓↓
1.不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事
2.如何才能成为优秀的架构师?
3.从零开始搭建创业公司后台技术栈
4.程序员一般可以从什么平台接私活?
5.37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...
6.IntelliJ IDEA 2019.3 首个最新访问版本发布,新特性抢先看
7.漫画:程序员相亲图鉴,笑屎我了~
8.15张图看懂瞎忙和高效的区别!
一个人学习、工作很迷茫?
点击「阅读原文」加入我们的小圈子!
Spring Boot 集成 WebSocket,轻松实现信息推送!相关推荐
- spring boot 集成 websocket 实现消息主动推送
前言 http协议是无状态协议,每次请求都不知道前面发生了什么,而且只可以由浏览器端请求服务器端,而不能由服务器去主动通知浏览器端,是单向的,在很多场景就不适合,比如实时的推送,消息通知或者股票等信息 ...
- SpringBoot2.x系列教程(四十五)Spring Boot集成WebSocket实现技术交流群功能
在上篇文章中,我们了解了WebSocket的基本功能及相关概念.本篇文章中我们以具体的实例来演示,在Spring Boot中整合WebSocket,同时实现一个场景的业务场景功能. 针对在Spring ...
- Spring Boot 集成 WebSocket通信信息推送!
一.什么是websocket? WebSocket 协议是基于 TCP 的一种新的网络协议. 它实现了客户端与服务器之间的全双工通信,学过计算机网络都知道,既然是全双工,就说明了服务器可以主动发送信息 ...
- java websocket注解_【websocket】spring boot 集成 websocket 的四种方式
集成 websocket 的四种方案 1. 原生注解 pom.xml org.springframework.boot spring-boot-starter-websocket WebSocketC ...
- SpringBoot 集成 WebSocket 实现消息群发推送
一. 什么是 WebSocket WebSocket 是一种全新的协议.它将 TCP 的 Socket(套接字)应用在了web page上,从而使通信双方建立起一个保持在活动状态的连接通道,并且属于全 ...
- Spring Boot入门教程(五十三): 极光推送Java-SDK
pom.xml <!-- 极光推送 begin --> <dependency><groupId>cn.jpush.api</groupId><a ...
- Spring Boot进阶之Web进阶 代码推送的github上面去
还是搜狗的输入法比较好 Exception.class 上面开不见的部分是这里的 代码上次github上面去保存起来 https://github.com/yangjiabinylg/girl2 h ...
- spring boot 集成socketIo 做消息推送
spring boot 集成socketIo 做消息推送 项目需求 代码展示 客户端代码 服务端代码 项目需求 后台管理系统用户小铃铛,消息推送功能并展示有多少条消息或者小红点 代码展示 客户端代码 ...
- Spring Boot 集成 Druid 监控数据源
关注"Java后端技术全栈" 回复"面试"获取全套大厂面试资料 Druid 介绍 Druid 是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池.插件框架和 ...
- Spring Boot教程(十六):Spring Boot集成shiro
Apache Shiro™是一个功能强大且易于使用的Java安全框架,可执行身份验证,授权,加密和会话管理.借助Shiro易于理解的API,您可以快速轻松地保护任何应用程序 - 从最小的移动应用程序到 ...
最新文章
- 成为优秀高级程序员的10个要点(转)
- JPA基础(一):全面阐释和精彩总结JPA
- 苹果回应中情局攻击事件:许多漏洞已经得到解决
- 后盾网lavarel视频项目---页面post方式提交之后动态弹出错误信息
- NOJ37 回文字符串---整理一下都是各种回文类型啊,
- 微软面试题目(一) 计算两个日期之间的天数
- do { ....} while(0) 在宏里冗余的意义
- CSS3中的display:grid网格布局介绍
- 第 5-6 课:Java 并发包中的高级同步工具 + 面试题
- JavaScript:综合案例---房贷计算器的实现
- csv数据源的创建(一)
- Android 设计模式:(一)策略模式 —— 封装行为的大局观
- 学习日记| javaScript在网页绘制国际象棋盘
- Java全栈工程实践
- 区块链学习笔记20——权益证明
- My Seventy-seventh Page - 零钱兑换 - By Nicolas
- vmware虚拟机连不上服务器,VMware虚拟机nat模式连不上网怎么办
- 绿城离职员工万言书全文,不看你后悔!当今很少有此类文言文!
- 面试技巧: 轻松过关10种方法
- 教你如何微信公众号图文中怎么下载封面图
热门文章
- vscode调试typescript
- SQL语句中AND OR运算符优先级
- ASP.NET MVC 解析模板生成静态页一(RazorEngine)
- Spring Cloud实战(六)-Spring Cloud Netflix Bus
- Postfix 电子邮件系统精要
- 《vSphere性能设计:性能密集场景下CPU、内存、存储及网络的最佳设计实践》一1.1.4 从默认值开始...
- React源码剖析系列 - 玩转 React Transition
- html的meta标签的作用
- angular之service、factory预provider区别
- 并查集详细讲解(转载) 模板