简介
所谓WebSocket, 类似于Socket,它的作用是可以让Web应用中的客户端和服务端建立全双工通信。在基于Spring的应用中使用WebSocket一般可以有以下三种方式:
- 使用Java提供的@ServerEndpoint注解实现
- 使用Spring提供的低层级WebSocket API实现
- 使用STOMP消息实现
下面,我将对这三种实现方式做一个简单介绍,此外有关WebSocket性质的更多介绍可以参考以下这篇文章:WebSocket探秘
注:本篇文章的完整源码可以参考:https://github.com/zifangsky/WebSocketDemo
使用Java提供的@ServerEndpoint注解实现
(1)使用@ServerEndpoint注解监听一个WebSocket请求路径:
/reverse
,并定义了如何处理客户端发来的消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package cn.zifangsky.samplewebsocket.websocket; import javax.websocket.OnMessage; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import java.io.IOException; /** * ReverseWebSocketEndpoint * * @author zifangsky * @date 2018/9/30 * @since 1.0.0 */ @ServerEndpoint("/reverse") public class ReverseWebSocketEndpoint { @OnMessage public void handleMessage(Session session, String message) throws IOException { session.getBasicRemote().sendText("Reversed: " + new StringBuilder(message).reverse()); } } |
(2)WebSocket相关配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package cn.zifangsky.samplewebsocket.config; import cn.zifangsky.samplewebsocket.websocket.ReverseWebSocketEndpoint; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * WebSocket相关配置 * * @author zifangsky * @date 2018/9/30 * @since 1.0.0 */ @Configuration @EnableWebSocket public class WebSocketConfig{ @Bean public ReverseWebSocketEndpoint reverseWebSocketEndpoint() { return new ReverseWebSocketEndpoint(); } @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } |
(3)示例页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | <html xmlns:th="http://www.thymeleaf.org"> <head> <meta content="text/html;charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>WebSocket Examples: Reverse</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script th:src="@{/layui/layui.js}"></script> <link th:href="@{/layui/css/layui.css}" rel="stylesheet"> <style type="text/css"> #connect-container { margin: 0 auto; width: 400px; } #connect-container div { padding: 5px; margin: 0 7px 10px 0; } .layui-btn { display: inline-block; } </style> <script type="text/javascript"> var ws = null; $(function () { var target = $("#target"); if (window.location.protocol === 'http:') { target.val('ws://' + window.location.host + target.val()); } else { target.val('wss://' + window.location.host + target.val()); } }); function setConnected(connected) { var connect = $("#connect"); var disconnect = $("#disconnect"); var reverse = $("#reverse"); if (connected) { connect.addClass("layui-btn-disabled"); disconnect.removeClass("layui-btn-disabled"); reverse.removeClass("layui-btn-disabled"); } else { connect.removeClass("layui-btn-disabled"); disconnect.addClass("layui-btn-disabled"); reverse.addClass("layui-btn-disabled"); } connect.attr("disabled", connected); disconnect.attr("disabled", !connected); reverse.attr("disabled", !connected); } //连接 function connect() { var target = $("#target").val(); ws = new WebSocket(target); ws.onopen = function () { setConnected(true); log('Info: WebSocket connection opened.'); }; ws.onmessage = function (event) { log('Received: ' + event.data); }; ws.onclose = function () { setConnected(false); log('Info: WebSocket connection closed.'); }; } //断开连接 function disconnect() { if (ws != null) { ws.close(); ws = null; } setConnected(false); } //文字反转 function reverse() { if (ws != null) { var message = $("#message").val(); log('Sent: ' + message); ws.send(message); } else { alert('WebSocket connection not established, please connect.'); } } //日志输出 function log(message) { console.debug(message); } </script> </head> <body> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div id="connect-container" class="layui-elem-field"> <legend>Reverse</legend> <div> <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/reverse"/> </div> <div> <button id="connect" class="layui-btn layui-btn-normal" onclick="connect();">Connect</button> <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="disconnect();">Disconnect </button> </div> <div> <textarea id="message" class="layui-textarea" placeholder="请输入需要反转的内容" style="width: 350px"></textarea> </div> <div> <button id="reverse" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="reverse();">Reverse message </button> </div> </div> </div> </body> </html> |
启动项目后访问页面,效果如下:
使用Spring提供的低层级WebSocket API实现
Spring 4.0为WebSocket通信提供了支持,包括:
发送和接收消息的低层级API;
发送和接收消息的高级API;
用来发送消息的模板;
支持SockJS,用来解决浏览器端、服务器以及代理不支持WebSocket的问题。
(1)添加一个WebSocketHandler:
AbstractWebSocketHandler
类的消息处理类,然后自定义对”建立连接“、”接收/发送消息“、”异常情况“等情况进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | package cn.zifangsky.samplewebsocket.websocket; import cn.zifangsky.samplewebsocket.service.EchoService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import javax.annotation.Resource; import java.text.MessageFormat; /** * 通过继承 {@link org.springframework.web.socket.handler.AbstractWebSocketHandler} 的示例 * * @author zifangsky * @date 2018/10/9 * @since 1.0.0 */ public class EchoWebSocketHandler extends TextWebSocketHandler{ private final Logger logger = LoggerFactory.getLogger(getClass()); @Resource(name = "echoServiceImpl") private EchoService echoService; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { logger.debug("Opened new session in instance " + this); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { //组装返回的Echo信息 String echoMessage = this.echoService.echo(message.getPayload()); logger.debug(MessageFormat.format("Echo message \"{0}\"", echoMessage)); session.sendMessage(new TextMessage(echoMessage)); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { session.close(CloseStatus.SERVER_ERROR); logger.debug("Info: WebSocket connection closed."); } } |
(2)WebSocket相关配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package cn.zifangsky.samplewebsocket.config; import cn.zifangsky.samplewebsocket.websocket.EchoWebSocketHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; /** * WebSocket相关配置 * * @author zifangsky * @date 2018/9/30 * @since 1.0.0 */ @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer{ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(echoWebSocketHandler(), "/echoMessage"); registry.addHandler(echoWebSocketHandler(), "/echoMessage_SockJS").withSockJS(); } /** * 通过继承 {@link org.springframework.web.socket.handler.AbstractWebSocketHandler} 的示例 */ @Bean public WebSocketHandler echoWebSocketHandler(){ return new EchoWebSocketHandler(); } } |
WebSocket
(也就是/echoMessage这个连接地址),还使用SockJS
配置了浏览器不支持WebSocket
技术时的替代方案(也就是/echoMessage_SockJS这个连接地址)。
(3)两个示例页面:
i)echo.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | <html xmlns:th="http://www.thymeleaf.org"> <head> <meta content="text/html;charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>WebSocket Examples: Reverse</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script th:src="@{/layui/layui.js}"></script> <link th:href="@{/layui/css/layui.css}" rel="stylesheet"> <style type="text/css"> #connect-container { margin: 0 auto; width: 400px; } #connect-container div { padding: 5px; margin: 0 7px 10px 0; } .layui-btn { display: inline-block; } </style> <script type="text/javascript"> var ws = null; $(function () { var target = $("#target"); if (window.location.protocol === 'http:') { target.val('ws://' + window.location.host + target.val()); } else { target.val('wss://' + window.location.host + target.val()); } }); function setConnected(connected) { var connect = $("#connect"); var disconnect = $("#disconnect"); var echo = $("#echo"); if (connected) { connect.addClass("layui-btn-disabled"); disconnect.removeClass("layui-btn-disabled"); echo.removeClass("layui-btn-disabled"); } else { connect.removeClass("layui-btn-disabled"); disconnect.addClass("layui-btn-disabled"); echo.addClass("layui-btn-disabled"); } connect.attr("disabled", connected); disconnect.attr("disabled", !connected); echo.attr("disabled", !connected); } //连接 function connect() { var target = $("#target").val(); ws = new WebSocket(target); ws.onopen = function () { setConnected(true); log('Info: WebSocket connection opened.'); }; ws.onmessage = function (event) { log('Received: ' + event.data); }; ws.onclose = function () { setConnected(false); log('Info: WebSocket connection closed.'); }; } //断开连接 function disconnect() { if (ws != null) { ws.close(); ws = null; } setConnected(false); } //Echo function echo() { if (ws != null) { var message = $("#message").val(); log('Sent: ' + message); ws.send(message); } else { alert('WebSocket connection not established, please connect.'); } } //日志输出 function log(message) { console.debug(message); } </script> </head> <body> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div id="connect-container" class="layui-elem-field"> <legend>Echo</legend> <div> <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/echoMessage"/> </div> <div> <button id="connect" class="layui-btn layui-btn-normal" onclick="connect();">Connect</button> <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="disconnect();">Disconnect </button> </div> <div> <textarea id="message" class="layui-textarea" placeholder="请输入请求的内容" style="width: 350px"></textarea> </div> <div> <button id="echo" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="echo();">Echo message </button> </div> </div> </div> </body> </html> |
ii)echo_sockjs.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | <html xmlns:th="http://www.thymeleaf.org"> <head> <meta content="text/html;charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>WebSocket Examples: Reverse</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script> <script th:src="@{/layui/layui.js}"></script> <link th:href="@{/layui/css/layui.css}" rel="stylesheet"> <style type="text/css"> #connect-container { margin: 0 auto; width: 400px; } #connect-container div { padding: 5px; margin: 0 7px 10px 0; } .layui-btn { display: inline-block; } </style> <script type="text/javascript"> var ws = null; $(function () { var target = $("#target"); if (window.location.protocol === 'http:') { target.val('http://' + window.location.host + target.val()); } else { target.val('https://' + window.location.host + target.val()); } }); function setConnected(connected) { var connect = $("#connect"); var disconnect = $("#disconnect"); var echo = $("#echo"); if (connected) { connect.addClass("layui-btn-disabled"); disconnect.removeClass("layui-btn-disabled"); echo.removeClass("layui-btn-disabled"); } else { connect.removeClass("layui-btn-disabled"); disconnect.addClass("layui-btn-disabled"); echo.addClass("layui-btn-disabled"); } connect.attr("disabled", connected); disconnect.attr("disabled", !connected); echo.attr("disabled", !connected); } //连接 function connect() { var target = $("#target").val(); ws = new SockJS(target); ws.onopen = function () { setConnected(true); log('Info: WebSocket connection opened.'); }; ws.onmessage = function (event) { log('Received: ' + event.data); }; ws.onclose = function () { setConnected(false); log('Info: WebSocket connection closed.'); }; } //断开连接 function disconnect() { if (ws != null) { ws.close(); ws = null; } setConnected(false); } //Echo function echo() { if (ws != null) { var message = $("#message").val(); log('Sent: ' + message); ws.send(message); } else { alert('WebSocket connection not established, please connect.'); } } //日志输出 function log(message) { console.debug(message); } </script> </head> <body> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div id="connect-container" class="layui-elem-field"> <legend>Echo With SockJS</legend> <div> <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/echoMessage_SockJS"/> </div> <div> <button id="connect" class="layui-btn layui-btn-normal" onclick="connect();">Connect</button> <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="disconnect();">Disconnect </button> </div> <div> <textarea id="message" class="layui-textarea" placeholder="请输入请求的内容" style="width: 350px"></textarea> </div> <div> <button id="echo" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="echo();">Echo message </button> </div> </div> </div> </body> </html> |
具体效果省略,可自行运行源码查看。
使用STOMP消息实现
所谓STOMP(Simple Text Oriented Messaging Protocol),就是在WebSocket基础之上提供了一个基于帧的线路格式(frame-based wire format)层。它对发送简单文本消息定义了一套规范格式(STOMP消息基于Text,当然也支持传输二进制数据),目前很多服务端消息队列都已经支持STOMP,比如:RabbitMQ、 ActiveMQ等。
(1)WebSocket相关配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package cn.zifangsky.stompwebsocket.config; import cn.zifangsky.stompwebsocket.interceptor.websocket.MyChannelInterceptor; import org.springframework.beans.factory.annotation.Autowired; 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.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; /** * WebSocket相关配置 * * @author zifangsky * @date 2018/9/30 * @since 1.0.0 */ @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{ @Autowired private MyChannelInterceptor myChannelInterceptor; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/stomp-websocket").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { //客户端需要把消息发送到/message/xxx地址 registry.setApplicationDestinationPrefixes("/message"); //服务端广播消息的路径前缀,客户端需要相应订阅/topic/yyy这个地址的消息 registry.enableSimpleBroker("/topic"); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(myChannelInterceptor); } } |
从上面代码可以看出,这里设置了好几个地址,简单解释如下:
首先注册了一个名为/stomp-websocket的端点,也就是STOMP客户端连接的地址。
此外,定义了服务端处理WebSocket消息的前缀是/message,这个地址用于客户端向服务端发送消息(比如客户端向/message/hello这个地址发送消息,那么服务端通过@MessageMapping(“/hello”)这个注解来接收并处理消息)
最后,定义了一个简单消息代理,也就是服务端广播消息的路径前缀(比如客户端监听/topic/greeting这个地址,那么服务端就可以通过@SendTo(“/topic/greeting”)这个注解向客户端发送STOMP消息)。
需要注意的是,上面代码中还添加了一个名为MyChannelInterceptor的拦截器,目的是为了在客户端断开连接后打印一下日志。相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | package cn.zifangsky.stompwebsocket.interceptor.websocket; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.stereotype.Component; import java.security.Principal; import java.text.MessageFormat; /** * 自定义{@link org.springframework.messaging.support.ChannelInterceptor},实现断开连接的处理 * * @author zifangsky * @date 2018/10/10 * @since 1.0.0 */ @Component public class MyChannelInterceptor implements ChannelInterceptor{ private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) { StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); StompCommand command = accessor.getCommand(); //用户已经断开连接 if(StompCommand.DISCONNECT.equals(command)){ String user = ""; Principal principal = accessor.getUser(); if(principal != null && StringUtils.isNoneBlank(principal.getName())){ user = principal.getName(); }else{ user = accessor.getSessionId(); } logger.debug(MessageFormat.format("用户{0}的WebSocket连接已经断开", user)); } } } |
(2)使用@MessageMapping和@SendTo注解处理消息:
@SendTo
注解则用于将服务端的消息发送给监听了该路径的客户端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package cn.zifangsky.stompwebsocket.controller; import cn.zifangsky.stompwebsocket.model.websocket.Greeting; import cn.zifangsky.stompwebsocket.model.websocket.HelloMessage; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; /** * Greeting * @author zifangsky * @date 2018/9/30 * @since 1.0.0 */ @Controller public class GreetingController { @MessageMapping("/hello") @SendTo("/topic/greeting") public HelloMessage greeting(Greeting greeting) { return new HelloMessage("Hello," + greeting.getName() + "!"); } } |
(3)示例页面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | <html xmlns:th="http://www.thymeleaf.org"> <head> <meta content="text/html;charset=UTF-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>WebSocket Examples: Reverse</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script> <script th:src="@{/layui/layui.js}"></script> <link th:href="@{/layui/css/layui.css}" rel="stylesheet"> <style type="text/css"> #connect-container { margin: 0 auto; width: 400px; } #connect-container div { padding: 5px; margin: 0 7px 10px 0; } .layui-btn { display: inline-block; } </style> <script type="text/javascript"> var stompClient = null; $(function () { var target = $("#target"); if (window.location.protocol === 'http:') { target.val('http://' + window.location.host + target.val()); } else { target.val('https://' + window.location.host + target.val()); } }); function setConnected(connected) { var connect = $("#connect"); var disconnect = $("#disconnect"); var echo = $("#echo"); if (connected) { connect.addClass("layui-btn-disabled"); disconnect.removeClass("layui-btn-disabled"); echo.removeClass("layui-btn-disabled"); } else { connect.removeClass("layui-btn-disabled"); disconnect.addClass("layui-btn-disabled"); echo.addClass("layui-btn-disabled"); } connect.attr("disabled", connected); disconnect.attr("disabled", !connected); echo.attr("disabled", !connected); } //连接 function connect() { var target = $("#target").val(); var ws = new SockJS(target); stompClient = Stomp.over(ws); stompClient.connect({}, function () { setConnected(true); log('Info: STOMP connection opened.'); //订阅服务端的/topic/greeting地址 stompClient.subscribe("/topic/greeting", function (greeting) { log('Received: ' + JSON.parse(greeting.body).content); }) },function () { //断开处理 setConnected(false); log('Info: STOMP connection closed.'); }); } //断开连接 function disconnect() { if (stompClient != null) { stompClient.disconnect(); stompClient = null; } setConnected(false); log('Info: STOMP connection closed.'); } //向服务端发送姓名 function sendName() { if (stompClient != null) { var username = $("#username").val(); log('Sent: ' + username); stompClient.send("/message/hello", {}, JSON.stringify({'name': username})); } else { alert('STOMP connection not established, please connect.'); } } //日志输出 function log(message) { console.debug(message); } </script> </head> <body> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div id="connect-container" class="layui-elem-field"> <legend>STOMP Message With SockJS</legend> <div> <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/stomp-websocket"/> </div> <div> <button id="connect" class="layui-btn layui-btn-normal" onclick="connect();">Connect</button> <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="disconnect();">Disconnect </button> </div> <div> <input id="username" type="text" class="layui-input" size="40" style="width: 350px" placeholder="请输入你的姓名" value=""/> </div> <div> <button id="echo" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled" onclick="sendName();">Say hello </button> </div> </div> </div> </body> </html> |
启动项目后访问页面,效果如下:
参考:
jht385 2019/09/01 15:26
老哥啊,写demo不要参杂那么多无关的东西啊,什么工具类,连数据库,认证授权 都来了,完全都没用上啊。然后页面里打印用的是,console.debug 我还是这次去查的,之前都是用console.log,居然连chrome都不支持 console.debug,你用它打印干嘛啊。本来想看看demo,删得我好心累啊
admin 博主 2019/09/01 15:45
@ 源码中的部分代码是后面示例需要用的,所以并不是无关紧要的东西。再说了,给出示例只是用来参考的,又不是让你直接抄代码,你都在写websocket了还不会基本的增删改查吗?这就有点说不过去了吧。 最后,你觉得心累你可以关了不看,我既没有从你那儿赚一分钱,又没有逼着你看,既然觉得累又何必自讨苦吃呢!
不悔 2018/12/17 14:47
可以给个sql脚本么,老哥
admin 博主 2018/12/18 09:38
@ 这几篇文章介绍的内容跟数据库没太大关系,你用你自己的SQL测试就行,或者不连数据库模拟登录吧。