SpringBoot本身不提供WebSocket支持,还是需要服务器如内嵌的Tomcat对WebSocket的支持。
引入maven依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency>
引入Spring Boot对WebSocket依赖实际上是Spring对WebSocket的支持:
客户端这里使用Sockjs,非原生的websocket支持的js API,这样做的好处是,对不支持WebSocket的浏览器可以自动降级。使用轮询的方式来访问服务端。
https://github.com/sockjs/sockjs-client
cdn加速:https://www.bootcdn.cn/sockjs-client/
HTML echo.html代码:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>WebSocket Simple</title> 5 <style type="text/css"> 6 #connect-container { 7 float: left; 8 width: 400px; 9 } 10 11 #connect-container div { 12 padding: 5px; 13 } 14 15 #console-container { 16 float: left; 17 margin-left: 15px; 18 width: 400px; 19 } 20 21 #console { 22 border: 1px solid #CCCCCC; 23 border-right-color: #999999; 24 border-bottom-color: #999999; 25 height: 170px; 26 overflow-y: scroll; 27 padding: 5px; 28 width: 100%; 29 } 30 31 #console p { 32 padding: 0; 33 margin: 0; 34 } 35 </style> 36 <script src="https://cdn.bootcss.com/sockjs-client/0.3.4/sockjs.min.js"></script> 37 <script type="text/javascript"> 38 var ws = null; 39 40 function setConnected(connected) { 41 document.getElementById('connect').disabled = connected; 42 document.getElementById('disconnect').disabled = !connected; 43 document.getElementById('echo').disabled = !connected; 44 } 45 46 function connect() { 47 var target = document.getElementById('target').value; 48 ws = new SockJS(target); 49 ws.onopen = function () { 50 setConnected(true); 51 log('Info: WebSocket connection opened.'); 52 }; 53 ws.onmessage = function (event) { 54 log('Received:' + event.data); 55 }; 56 ws.onclose = function () { 57 setConnected(false); 58 log('Info: WebSocket connection closed.') 59 } 60 } 61 62 function disconnect() { 63 if(ws != null){ 64 ws.close(); 65 ws = null; 66 } 67 setConnected(false); 68 } 69 70 function echo() { 71 if(ws != null){ 72 var message = document.getElementById('message').value; 73 log('Send:' + message); 74 ws.send(message); 75 }else{ 76 alert('WebSocket connection not established , plase connect.'); 77 } 78 } 79 80 function log(message) { 81 var console = document.getElementById('console'); 82 console.appendChild(document.createTextNode('<p>'+message+'</p>')); 83 while(console.childNodes.length > 25){ 84 console.removeChild(console.firstChild); 85 } 86 console.scrollTop = console.scrollHeight; 87 } 88 </script> 89 </head> 90 <body> 91 <noscript> 92 <h2 >............................................... </h2> 93 </noscript> 94 <div> 95 <div id="connect-container"> 96 <div> 97 <input id="target" type="text" size="40" style="" value="/echo"/> 98 </div> 99 <div> 100 <button id = "connect" onclick="connect();">Connect</button> 101 <button id="disconnect" onclick="disconnect();" disabled="disabled">Disconnect</button> 102 </div> 103 <div> 104 <textarea id="message" >a message to be sent</textarea> 105 </div> 106 <div> 107 <button id="echo" onclick="echo();" disabled="disabled">Echo message</button> 108 </div> 109 </div> 110 <div id="console-container"> 111 <div id="console"></div> 112 </div> 113 </div> 114 </body> 115 </html>
下面Spring Boot编写服务端代码:
首先定义一个业务接口,获取消息:
public interface EchoService { String getMessage(String message); }
然后提供一个业务接口的实现类:
public class DefaultEchoService implements EchoService { private final String echoFormat; public DefaultEchoService(String echoFormat) { this.echoFormat = (null != echoFormat)?echoFormat:"%s"; } @Override public String getMessage(String message) { return String.format(echoFormat,message); } }
然后继承一个websocket消息处理的handler(类似于netty对websocket的处理):TextWebSocketHandler
public class EchoWebSocketHandler extends TextWebSocketHandler { private EchoService echoService; public EchoWebSocketHandler(EchoService echoService) { this.echoService = echoService; } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("连接建立"); } /** * 类似于Netty的Channel * @param session * @param exception * @throws Exception */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { session.close(CloseStatus.SERVER_ERROR); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String echoMessage = this.echoService.getMessage(message.getPayload()); //消息重新发回给客户端 session.sendMessage(new TextMessage(echoMessage)); } }
继承关系类图如下,典型的适配器模式:
最后在启动类实现WebSocketConfigurer接口,然后把自己实现的EchoWebSocketHandler注册进去
@SpringBootApplication
@EnableWebSocket //非常重要,不然不会启用websocket功能 public class SpringbootDemoApplication implements WebSocketConfigurer { private static final Logger logger = LoggerFactory.getLogger(SpringbootDemoApplication.class); public static void main(String[] args) { SpringApplication.run(SpringbootDemoApplication.class, args); } @PostConstruct public void myLog(){ logger.trace("trace Message"); logger.debug("debug Message"); logger.info("info Message"); logger.warn("warn Message"); logger.error("error Message"); } /** * 注册websocket的处理器:EchoWebSocketHandler * @param registry */ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(echoWebSocketHandler(),"/echo").withSockJS(); } /** * 实例化 * @return */ @Bean public EchoService echoService(){ return new DefaultEchoService(" output message: "%s"! "); } @Bean public WebSocketHandler echoWebSocketHandler(){ return new EchoWebSocketHandler(echoService()); } }
测试验证:
访问:http://localhost:8090/echo.html 页面代码
Status Code 101 代表协议转换,Http升级成WebSocket协议