spring springboot2 结合 websocket+sockJs+stomp 实现个人订阅和广播模式
一 名词解释
1.WebSocket
WebSocket 是发送和接收消息的底层API,WebSocket 协议提供了通过一个套接字实现全双工通信的功能。也能够实现 web 浏览器和 server 间的异步通信,全双工意味着 server 与浏览器间可以发送和接收消息。需要注意的是必须考虑浏览器是否支持
2.SockJs
为了应对许多浏览器不支持WebSocket协议的问题,设计了备选SockJs。
SockJS 是 WebSocket 技术的一种模拟。SockJS 会 尽可能对应 WebSocket API,但如果 WebSocket 技术不可用的话,就会选择另外的通信方式协议。
3.STOMP
SockJS 为 WebSocket 提供了 备选方案。但无论哪种场景,对于实际应用来说,这种通信形式层级过低。下面看一下如何 在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server 间的 通信增加适当的消息语义。(STOMP—— Simple Text Oriented Message Protocol——面向消息的简单文本协议)
4.三者之间的关系
WebSocket 是底层协议,SockJS 是WebSocket 的备选方案,也是 底层协议,而 STOMP 是基于 WebSocket(SockJS) 的上层协议
二 springboot的config
<!-- websocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
1.基础配置
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic", "/queue", "/user"); //表示在topic和queue这两个域上服务端可以向客户端发消息 registry.setApplicationDestinationPrefixes("/app");//客户端向服务器端发送时的主题上面需要加"/app"作为前缀 registry.setUserDestinationPrefix("/user");//指定用户发送一对一的主题,前缀是"/user" } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/stomp/endpoint").withSockJS(); } }
第一步就是添加注解 此注解 这个配置类不仅配置了 WebSocket,还配置了基于代理的 STOMP 消息
第二步 配置一个消息代理..一个是允许服务器向客户端发送的域 一个是客户端想服务器发送的前缀,最后一个是指定一对第一发送的前缀
第三步 就是配置了节点,并且使用sockjs的方式
2.监听连接与断开
用到了redis管理session
@Access(name = "监听websocket连接成功") @EventListener public void onConnectEvent(SessionConnectEvent event) { //获取Session连接信息 StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //获取SessionId String sessionId = sha.getSessionId(); //存储redis Map<String, String> map = new HashMap<String, String>(); map.put(sessionId, agentNo); redisTemplate.opsForHash().putAll("webSocket", map); } @Access(name = "监听websocket断开连接") @EventListener public void onDisconnectEvent(SessionDisconnectEvent event) { //获取Session连接信息 StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //获取SessionId String sessionId = sha.getSessionId(); //redis删除此sessionId redisTemplate.opsForHash().delete("webSocket", sessionId); } }
三.实现前端代码
1.引入js
<!-- websocket ****代表自己的项目路径 这个写自己的 --> <script src="/***/sockjs-client/sockjs.min.js"></script> <script src="/***/stomp-websocket/stomp.min.js"></script> <!-- 参考 --> <!-- https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.4.0/sockjs.min.js https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js -->
2.连接和订阅消息
使用了agentNo 区分订阅广播消息与个人消息
var stompClient = null; var socket = null; function connect() { //从sesionStorage获取坐席工号 下面连接头需要 这个按照自己需求修改 agentNo = sessionStorage.getItem("agentNo"); //建立连接对象(还未发起连接) 地址更换成自己的 连接服务器注册端点endPoint时,写访问服务器的全路径URL: socket = new SockJS('地址'); //获取 STOMP 子协议的客户端对象 stompClient = Stomp.over(socket); //建立连接,并且连接头上定义了坐席工号 stompClient.connect({ userName: agentNo // <---这是头 在后面的监听 连接成功的类可以获取到 业务代码需要 按需删除 }, function (frame) { console.log('Connected: ' + frame); //订阅广播消息 stompClient.subscribe('/topic/greetings', function (greeting) { var data = JSON.parse(greeting.body).data; //实现自己的需求 }); //订阅个人消息 stompClient.subscribe('/user/' + agentNo + '/message', function (greeting) { var data = JSON.parse(greeting.body); //实现自己的需求 }); }); }
2.发送消息
function send(data) { var param = { id: data, release: true } stompClient.send("/app/release", {}, JSON.stringify(param)); // <----- app 就是在config中定义的客户端往服务器发送的前缀,param是信息.必须用实体发送后台必须用实体接收 //release是后面controller中定义的 相当于RequestMapping中写的地址一样 }
四.实现后端代码
1.发送广播
@Access(name = "发布公告") @MessageMapping("/release") @SendTo("/topic/greetings") public WebResult release(@RequestBody Announcement announcement) { //执行业务代码逻辑 }
2.发送个人消息
@Autowired private SimpMessagingTemplate messagingTemplate; public void demo() { // messagingTemplate.convertAndSend(url,""); //方法1 //方法2 相当于/user/' + agentNo + '/message' 对应前端订阅的地址 messagingTemplate.convertAndSendToUser(ledgerQuery.getAgentNo(), "/message", ""); }