<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
server: port: 80 #添加Thymeleaf配置 thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html mode: HTML5 encoding: UTF-8 content-type: text/html
package com.example.admin.config; import com.example.admin.utils.UserInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; /** * @Description: registerStompEndpoints(StompEndpointRegistry registry) configureMessageBroker(MessageBrokerRegistry config) 这个方法的作用是定义消息代理,通俗一点讲就是设置消息连接请求的各种规范信息。 registry.enableSimpleBroker("/topic")表示客户端订阅地址的前缀信息,也就是客户端接收服务端消息的地址的前缀信息(比较绕,看完整个例子,大概就能明白了) registry.setApplicationDestinationPrefixes("/app")指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀 */ @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { // 这个方法的作用是添加一个服务端点,来接收客户端的连接。 // registry.addEndpoint("/socket")表示添加了一个/socket端点,客户端就可以通过这个端点来进行连接。 // withSockJS()的作用是开启SockJS支持, // setAllowedOrigins 跨域问题 @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //注册两个STOMP的endpoint,分别用于广播和点对点 registry.addEndpoint("/webServer").setAllowedOrigins("*").withSockJS(); registry.addEndpoint("/queueServer").setAllowedOrigins("*").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { //表示客户端订阅地址的前缀信息,也就是客户端接收服务端消息的地址的前缀信息,topic用来广播,user用来实现p2p registry.enableSimpleBroker("/topic", "/user"); //指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀 registry.setApplicationDestinationPrefixes("/app"); //registry.setUserDestinationPrefix("/user");这句话表示给指定用户发送一对一的主题前缀是"/user"。 } /** * 配置客户端入站通道拦截器 */ @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(userInterceptor()); } /** * @return * @Title: createUserInterceptor * @Description: 将客户端渠道拦截器加入spring ioc容器 */ @Bean public UserInterceptor userInterceptor() { return new UserInterceptor(); } }
package com.example.admin.utils; import com.example.admin.model.User; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptorAdapter; import org.springframework.messaging.support.MessageHeaderAccessor; import java.util.LinkedList; import java.util.Map; public class UserInterceptor extends ChannelInterceptorAdapter { /** * 获取包含在stomp中的用户信息 */ @SuppressWarnings("rawtypes") @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS); if (raw instanceof Map) { Object name = ((Map) raw).get("name"); if (name instanceof LinkedList) { // 设置当前访问器的认证用户 accessor.setUser(new User(((LinkedList) name).get(0).toString())); } } } return message; } }
package com.example.admin.web; import com.example.admin.model.ReceiveMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.user.SimpUser; import org.springframework.messaging.simp.user.SimpUserRegistry; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/restful") public class RestFulController { @Autowired public SimpMessagingTemplate template; @Autowired private SimpUserRegistry userRegistry; /** * @Description:广播 * @Author:hui.yunfei@qq.com * @Date: 2019/6/4 */ @MessageMapping("/subscribe") //@SendTo public void subscribe(ReceiveMessage rm) { System.out.println("服务端接收到广播消息:"+rm); for(int i =1;i<=5;i++) { //广播使用convertAndSend方法,第一个参数为目的地,和js中订阅的目的地要一致 template.convertAndSend("/topic/getResponse", rm.getMsg()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * @Description:点对点 * @Author:hui.yunfei@qq.com * @Date: 2019/6/4 */ @MessageMapping("/queue") //@SendToUser public void queue(ReceiveMessage rm) { System.out.println("服务端接收到点对点消息:"+rm); for(int i =1;i<=5;i++) { /*广播使用convertAndSendToUser方法,第一个参数为用户id,此时js中的订阅地址为 "/user/" + 用户Id + "/message",其中"/user"是固定的*/ template.convertAndSendToUser(rm.getUser(),"/message",rm.getMsg()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * @Description:主动推消息到客户端(可配做定时器) * @Author:hui.yunfei@qq.com * @Date: 2019/6/4 */ @RequestMapping("/send") public void send(){ template.convertAndSend("/topic/getResponse", "我是服务器主动推送的消息"); } @RequestMapping("/getOnline") @ResponseBody public void getOnline(){ System.out.println("当前在线人数:" + userRegistry.getUserCount()); int i = 1; for (SimpUser user : userRegistry.getUsers()) { System.out.println("用户" + i++ + "---" + user); } } }
<html> <head> <meta charset="UTF-8"> <title>Hello topic</title> <script src="sockjs.min.js"></script> <script src="stomp.min.js"></script> <script src="jquery-3.2.1.min.js"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected){ document.getElementById("connect").disabled = connected; document.getElementById("disconnect").disabled = !connected; $("#response").html(); } function connect() { var socket = new SockJS("/webServer"); stompClient = Stomp.over(socket); stompClient.connect({name:‘yunfei‘}, function(frame) { setConnected(true); console.log(‘Connected: ‘ + frame); stompClient.subscribe(‘/topic/getResponse‘, function(response){ var response1 = document.getElementById(‘response‘); var p = document.createElement(‘p‘); p.style.wordWrap = ‘break-word‘; p.appendChild(document.createTextNode(response.body)); response1.appendChild(p); }); }); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendName() { var name = document.getElementById(‘name‘).value; console.info(1111111111); stompClient.send("/app/subscribe", {}, JSON.stringify({ ‘msg‘: name })); } </script> </head> <body onload="disconnect()"> <noscript><h2 style="color: #ff0000">Seems your browser doesn‘t support Javascript! Websocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div> <button id="connect" onclick="connect();">Connect</button> <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button> </div> <div id="conversationDiv"> <labal>名字</labal><input type="text" id="name" /> <button id="sendName" onclick="sendName();">Send</button> <p id="response"></p> </div> </div> </body> </html>
<html> <head> <meta charset="UTF-8"> <title>Hello queue</title> <script src="sockjs.min.js"></script> <script src="stomp.min.js"></script> <script src="jquery-3.2.1.min.js"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected){ document.getElementById("connect").disabled = connected; document.getElementById("disconnect").disabled = !connected; $("#response").html(); } function connect() { var socket = new SockJS("/queueServer"); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { setConnected(true); console.log(‘Connected: ‘ + frame); stompClient.subscribe(‘/user/‘+document.getElementById(‘user‘).value+‘/message‘, function(response){ var response1 = document.getElementById(‘response‘); var p = document.createElement(‘p‘); p.style.wordWrap = ‘break-word‘; p.appendChild(document.createTextNode(response.body)); response1.appendChild(p); }); }); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendName() { var name = document.getElementById(‘name‘).value; var user = document.getElementById(‘user‘).value; console.info(1111111111); stompClient.send("/app/queue", {}, JSON.stringify({ ‘msg‘: name,‘user‘:user})); } </script> </head> <body onload="disconnect()"> <noscript><h2 style="color: #ff0000">Seems your browser doesn‘t support Javascript! Websocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div> <labal>用户</labal><input type="text" id="user" /> <button id="connect" onclick="connect();">Connect</button> <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button> </div> <div id="conversationDiv"> <labal>名字</labal><input type="text" id="name" /> <button id="sendName" onclick="sendName();">Send</button> <p id="response"></p> </div> </div> </body> </html>
原文:https://www.cnblogs.com/jerry-wei/p/12849285.html