赞
踩
sockjs-client是从SockJS中分离出来的用于客户端使用的通信模块.所以我们就直接来看看SockJS. SockJS是一个浏览器的JavaScript库,它提供了一个类似于网络的对象,SockJS提供了一个连贯的,跨浏览器的JavaScriptAPI,它在浏览器和Web服务器之间创建了一个低延迟,全双工,跨域通信通道. 你可能会问,我为什么不直接用原生的WebSocket而要使用SockJS呢?这得益于SockJS的一大特性,一些浏览器中缺少对WebSocket的支持,因此,回退选项是必要的,而Spring框架提供了基于SockJS协议的透明的回退选项。SockJS提供了浏览器兼容性,优先使用原生的WebSocket,如果某个浏览器不支持WebSocket,SockJS会自动降级为轮询.
SockJS模仿WebSockets API,但它不是WebSocket,而是一个SockJS Javascript对象。首先,您需要加载SockJS JavaScript库。例如,你可以把它放在你的HTML head里:
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的简单文本协议; WebSocket是一个消息架构,不强制使用任何特定的消息协议,它依赖于应用层解释消息的含义. 与HTTP不同,WebSocket是处在TCP上非常薄的一层,会将字节流转化为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此,可以在 WebSocket 之上使用STOMP协议,来为浏览器 和 server间的 通信增加适当的消息语义。
创建spring boot 项目,并且依赖勾选 Thymeleaf
(前端使用的是模板引擎,你也可以些其他的,只要导入sockjs库就行)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--websocket start--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!--websocket end--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> <scope>provided</scope> </dependency> </dependencies>
WebSocket
配置import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler; /** * @Author: OF //作者及 * @Date: 2019-09-04 10:00//完成日期 * @Description: //初始化WebSocket * @Version: // 版本信息 * @Function // 主要函数及其功能 * @Others: // 其它内容的说明 * @History: // 历史修改记录 */ @Configuration /** * @EnableWebSocketMessageBroker * 开启使用 STOMP 协议来传输基于代理的消息,Broker是代理 */ @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { /** * setErrorHandler: 设置一个错误处理的 Handler, 以便捕捉错误信息 * addEndpoint: 切入点, 客户端在 new SockJs 的时候用到 * setAllowedOrigins:设置为「*」表示接收 http 和 https 的请求 * withSockJS: 使用 SockJS */ registry.setErrorHandler(this.webSocketHandler()) .addEndpoint("/endpointNiu") .setAllowedOrigins("*") .withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { /** * 参数是多个 destinationPrefixes, 服务端发送消息的 destination 要有这些前缀 */ registry.enableSimpleBroker("/topic", "/queue"); /** * 设置点对点时, destination 的前缀, 如客户端订阅 */ registry.setUserDestinationPrefix("/user"); } /** * WebSocket Error 处理 * * @return WebSocket Error 处理器 */ @Bean public StompSubProtocolErrorHandler webSocketHandler() { return new WebSocketErrorHandler(); } }
WebSocketErrorHandler.java
import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.Message; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler; /** * @Author: OF //作者及 * @Date: 2019-09-04 10:18//完成日期 * @Description: // 描述 * @Version: // 版本信息 * @Function // 主要函数及其功能 * @Others: // 其它内容的说明 * @History: // 历史修改记录 */ @Slf4j public class WebSocketErrorHandler extends StompSubProtocolErrorHandler { public WebSocketErrorHandler() { super(); } @Override public Message<byte[]> handleClientMessageProcessingError(Message<byte[]> clientMessage, Throwable ex) { log.error("handleClientMessageProcessingError:clientMessage-" + clientMessage + ", error-"+ex.getMessage()); return super.handleClientMessageProcessingError(clientMessage, ex); } @Override public Message<byte[]> handleErrorMessageToClient(Message<byte[]> errorMessage) { log.error("handleErrorMessageToClient:errorMessage-" + errorMessage); return super.handleErrorMessageToClient(errorMessage); } @Override protected Message<byte[]> handleInternal(StompHeaderAccessor errorHeaderAccessor, byte[] errorPayload, Throwable cause, StompHeaderAccessor clientHeaderAccessor) { log.error("handleInternal:errorHeaderAccessor-" + errorHeaderAccessor + ", errorPayload-" + errorPayload + ", error-" + cause.getMessage() + ", clientHeaderAccessor-"+clientHeaderAccessor); return super.handleInternal(errorHeaderAccessor, errorPayload, cause, clientHeaderAccessor); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessageType; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @Author: OF //作者及 * @Date: 2019-09-04 10:24//完成日期 * @Description: // 描述 * @Version: // 版本信息 * @Function // 主要函数及其功能 * @Others: // 其它内容的说明 * @History: // 历史修改记录 */ @Controller @Slf4j public class WebController { @Autowired private SimpMessagingTemplate messagingTemplate; /** * 接收消息 * @param name 姓名 * @return welcome, [姓名] ! * * @MessageMapping 类似于 @RequestMapping, 只不过映射的是 webSocket 的请求地址 * @SendTo("/topic/getBro") 指定该方法响应给哪个 topic, 客户端订阅了 /topic/getBro 的都能收到方法响应 */ @MessageMapping("/welcome") @SendTo("/topic/getBro") public String say(String name) { log.info("name: " + name); return "welcome, " + name + " !"; } /** * 广播式发送消息给订阅了「/topic/getBro」的客户端 */ @RequestMapping("sendMsgBro") @ResponseBody public void sendMsg() { messagingTemplate.convertAndSend("/topic/getBro", "服务器主动推送的广播消息"); } /** * 发送消息给指定 sessionId 的客户端, 且该客户端订阅了「/topic/getBro」 * * @param sessionId 客户端的 sessionId */ @RequestMapping("sendMsgPoint") @ResponseBody public void sendMsgPoint(String sessionId) { messagingTemplate.convertAndSendToUser(sessionId, "/queue/getPoint", "服务器主动推送的点对点消息", createHeaders(sessionId)); } private MessageHeaders createHeaders(String sessionId) { SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE); headerAccessor.setSessionId(sessionId); headerAccessor.setLeaveMutable(true); return headerAccessor.getMessageHeaders(); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.stereotype.Component; import org.springframework.web.socket.messaging.SessionConnectEvent; /** * @Author: OF //作者及 * @Date: 2019-09-04 10:27//完成日期 * @Description: // 新客户端连接监听器 * @Version: // 版本信息 * @Function // 主要函数及其功能 * @Others: // 其它内容的说明 * @History: // 历史修改记录 */ @Slf4j @Component public class WebSocketConnectListener implements ApplicationListener<SessionConnectEvent> { @Override public void onApplicationEvent(SessionConnectEvent sessionConnectEvent) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(sessionConnectEvent.getMessage()); String sessionId = accessor.getSessionId(); log.info("sessionId: {} 已连接", sessionId); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationListener; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.stereotype.Component; import org.springframework.web.socket.messaging.SessionDisconnectEvent; /** * @Author: OF //作者及 * @Date: 2019-09-04 10:31//完成日期 * @Description: // 断开连接监听器 * @Version: // 版本信息 * @Function // 主要函数及其功能 * @Others: // 其它内容的说明 * @History: // 历史修改记录 */ @Slf4j @Component public class WebSocketDisconnectListener implements ApplicationListener<SessionDisconnectEvent> { @Override public void onApplicationEvent(SessionDisconnectEvent event) { StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); String sessionId = sha.getSessionId(); log.info("sessionId: {} 已断开", sessionId); } }
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>WebSocket</title> <script th:src="@{js/sockjs.min.js}"></script> <script th:src="@{js/stomp.js}"></script> <script th:src="@{js/jquery-3.1.1.js}"></script> </head> <body onload="disconnect()"> <noscript><h2 style="color: #e80b0a;">Sorry,浏览器不支持WebSocket</h2></noscript> <div> <div> <button id="connect" onclick="connect();">连接</button> <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button> </div> <div id="conversationDiv"> <label>输入你的名字</label><input type="text" id="name"/> <button id="sendName" onclick="sendName();">发送</button> <p id="response"></p> </div> </div> <script type="text/javascript"> var stompClient = null; function setConnected(connected) { document.getElementById("connect").disabled = connected; document.getElementById("disconnect").disabled = !connected; document.getElementById("conversationDiv").style.visibility = connected ? 'visible' : 'hidden'; // $("#connect").disabled = connected; // $("#disconnect").disabled = !connected; $("#response").html(); } function connect() { var socket = new SockJS('/endpointNiu'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true); console.log('Connected:' + frame); // 订阅 /user/queue/getPoint stompClient.subscribe('/user/queue/getPoint', function (response) { showResponse("getPoint " + response.body); }); // 订阅 /topic/getBro stompClient.subscribe('/topic/getBro', function (response) { showResponse("getBro " + response.body); }) }); } // 断开连接 function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log('Disconnected'); } // 向服务器发送 function sendName() { var name = $('#name').val(); console.log('name:' + name); stompClient.send("/welcome", {}, name); } // 显示message function showResponse(message) { $("#response").html(message); } </script> </body> </html>
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; /** * @Author: OF //作者及 * @Date: 2019-09-04 10:41//完成日期 * @Description: // 描述 * @Version: // 版本信息 * @Function // 主要函数及其功能 * @Others: // 其它内容的说明 * @History: // 历史修改记录 */ @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/ws").setViewName("/ws"); } @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/js/**") .addResourceLocations("classpath:/static/js/"); } }
以上代码借鉴该文章
// 安装并引入相关模块 import SockJS from 'sockjs-client'; import Stomp from 'stompjs'; export default { data() { return { dataList: [] }; }, mounted:function(){ this.initWebSocket(); }, beforeDestroy: function () { // 页面离开时断开连接,清除定时器 this.disconnect(); clearInterval(this.timer); }, methods: { initWebSocket() { this.connection(); let self = this; // 断开重连机制,尝试发送消息,捕获异常发生时重连 this.timer = setInterval(() => { try { self.stompClient.send("test"); } catch (err) { console.log("断线了: " + err); self.connection(); } }, 5000); }, removeTab(targetName) { console.log(targetName) }, connection() { // 建立连接对象 this.socket = new SockJS('http://xxxxxx:8089/ws');//连接服务端提供的通信接口,连接以后才可以订阅广播消息和个人消息 // 获取STOMP子协议的客户端对象 this.stompClient = Stomp.over(this.socket); // 定义客户端的认证信息,按需求配置 var headers = { login: 'mylogin', passcode: 'mypasscode', // additional header 'client-id': 'my-client-id' }; // 向服务器发起websocket连接 this.stompClient.connect(headers,(frame) => { this.stompClient.subscribe('/topic/chat_msg', (msg) => { // 订阅服务端提供的某个topic consolel.log(msg.body); // msg.body存放的是服务端发送给我们的信息 }); }, (err) => { // 连接发生错误时的处理函数 console.log(err); }); }, // 断开连接 disconnect() { if (this.stompClient != null) { this.stompClient.disconnect(); console.log("Disconnected"); } } } };
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。