一、什么是WebSocket
WebSocket为浏览器和服务器提供异步双工的功能,浏览器可以向服务器发送消息,服务器也可以向浏览器发送消息,通过socket实现。直接使用WebSocket或者SockJS(WebSocket的模拟:增加了当浏览器不支持WebSocket时的支持)比较麻烦,一般使用STOMP协议(WebSocket的子协议,一个更高级的协议),此协议使用了一个基于贞(frame)的格式来定义消息,与HTTP的request和response相似。
二、Spring Boot 对WebSocket的支持
Spring Boot 提供的 starter pom 为spring-boot-starter-websocket 。
三、示例
1、广播式
1>配置WebSocket
package com.sezioo.ch7_6; import org.springframework.context.annotation.Configuration; 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; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{ @Override // 配置消息代理 public void configureMessageBroker(MessageBrokerRegistry registry) { //广播式配置一个/topic消息代理 registry.enableSimpleBroker("/topic"); } @Override // 注册STOMP协议的节点,并映射对应的URL public void registerStompEndpoints(StompEndpointRegistry registry) { //指定使用SockJS协议 registry.addEndpoint("/endPointSezioo").withSockJS(); } }
2>浏览器向服务器发送的消息用此类接收
package com.sezioo.ch7_6.domain; public class SeziooMessage { private String name; public String getName(){ return this.name; } }
3>服务器向浏览器返回的消息类
package com.sezioo.ch7_6.domain; public class SeziooResponse { private String responseMessage; public SeziooResponse(String responseMessage) { this.responseMessage = responseMessage; } public String getResponseMessage() { return responseMessage; } }
4>控制器
package com.sezioo.ch7_6.web; import com.sezioo.ch7_6.domain.SeziooMessage; import com.sezioo.ch7_6.domain.SeziooResponse; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class SeController { @MessageMapping("/welcome") @SendTo("/topic/getResponse") public SeziooResponse say(SeziooMessage message)throws Exception{ Thread.sleep(3000); return new SeziooResponse("Welcome, "+message.getName()+"!"); } }
浏览器向服务器发送请求是,会通过@MessageMapping映射到对应方法下进行处理。
当服务器有消息时,会向订阅了@SendTo路径的浏览器发送消息。
5>前端页面
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Spring Boot +WebSocket+广播式</title> </head> <body οnlοad="disconnect();"> <noscript><h2 style="color: #ff0000;">貌似你的浏览器不支持websocket</h2></noscript> <div> <div> <button id="connect" οnclick="connect();">连接</button> <button id="disconnect" οnclick="disconnect();" disabled="disabled">断开连接</button> </div> <div id="conversationDiv"> <label>输入你的名字</label><input type="text" id="name"> <button id="sendName" οnclick="sendName();">发送</button> <p id="response"></p> </div> </div> <script th:src="@{sockjs.min.js}"></script> <script th:src="@{stomp.min.js}"></script> <script th:src="@{jquery-1.8.3.min.js}"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected){ document.getElementById("connect").disabled = connected; document.getElementById("disconnect").disabled = !connected; document.getElementById("disconnect").style.visibility = connected ? 'visible' : 'hidden'; $("#response").html(); } function connect(){ var socket = new SockJS('/endPointSezioo'); stompClient = Stomp.over(socket); stompClient.connect({},function(frame){ setConnected(true); console.log('Connected:'+frame); stompClient.subscribe('/topic/getResponse',function(response){ showResponse(JSON.parse(response.body).responseMessage); }); }); } function disconnect(){ if(stompClient!=null){ stompClient.disconnect(); } setConnected(false); console.log('Disconnected!'); } function sendName(){ var name = $('#name').val(); stompClient.send('/welcome',{},JSON.stringify({'name': name})); } function showResponse(message){ var response = $("#response"); response.html(message); } </script> </body> </html>
6>配置viewController
package com.sezioo.ch7_6.web; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class WebMVCConfig extends WebMvcConfigurerAdapter{ public void addViewControllers(ViewControllerRegistry registry){ registry.addViewController("/se").setViewName("/se"); } }