一 名詞解釋
1.WebSocket
WebSocket 是發送和接收消息的底層API,WebSocket 協議提供了通過一個套接字實現全雙工通信的功能。也能夠實現 web 瀏覽器和 server 間的異步通信,全雙工意味着 server 與瀏覽器間可以發送和接收消息。需要注意的是必須考慮瀏覽器是否支持
2.SockJs
為了應對許多瀏覽器不支持WebSocket協議的問題,設計了備選SockJs。
SockJS 是 WebSocket 技術的一種模擬。SockJS 會 盡可能對應 WebSocket API,但如果 WebSocket 技術不可用的話,就會選擇另外的通信方式協議。
3.STOMP
SockJS 為 WebSocket 提供了 備選方案。但無論哪種場景,對於實際應用來說,這種通信形式層級過低。下面看一下如何 在 WebSocket 之上使用 STOMP協議,來為瀏覽器 和 server 間的 通信增加適當的消息語義。(STOMP—— Simple Text Oriented Message Protocol——面向消息的簡單文本協議)
4.三者之間的關系
WebSocket 是底層協議,SockJS 是WebSocket 的備選方案,也是 底層協議,而 STOMP 是基於 WebSocket(SockJS) 的上層協議
二 springboot的config
<!-- websocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
1.基礎配置
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic", "/queue", "/user"); //表示在topic和queue這兩個域上服務端可以向客戶端發消息 registry.setApplicationDestinationPrefixes("/app");//客戶端向服務器端發送時的主題上面需要加"/app"作為前綴 registry.setUserDestinationPrefix("/user");//指定用戶發送一對一的主題,前綴是"/user" } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/stomp/endpoint").withSockJS(); } }
第一步就是添加注解 此注解 這個配置類不僅配置了 WebSocket,還配置了基於代理的 STOMP 消息
第二步 配置一個消息代理..一個是允許服務器向客戶端發送的域 一個是客戶端想服務器發送的前綴,最后一個是指定一對第一發送的前綴
第三步 就是配置了節點,並且使用sockjs的方式
2.監聽連接與斷開
用到了redis管理session
@Access(name = "監聽websocket連接成功") @EventListener public void onConnectEvent(SessionConnectEvent event) { //獲取Session連接信息 StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //獲取SessionId String sessionId = sha.getSessionId(); //存儲redis Map<String, String> map = new HashMap<String, String>(); map.put(sessionId, agentNo); redisTemplate.opsForHash().putAll("webSocket", map); } @Access(name = "監聽websocket斷開連接") @EventListener public void onDisconnectEvent(SessionDisconnectEvent event) { //獲取Session連接信息 StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage()); //獲取SessionId String sessionId = sha.getSessionId(); //redis刪除此sessionId redisTemplate.opsForHash().delete("webSocket", sessionId); } }
三.實現前端代碼
1.引入js
<!-- websocket ****代表自己的項目路徑 這個寫自己的 --> <script src="/***/sockjs-client/sockjs.min.js"></script> <script src="/***/stomp-websocket/stomp.min.js"></script> <!-- 參考 --> <!-- https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.4.0/sockjs.min.js https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js -->
2.連接和訂閱消息
使用了agentNo 區分訂閱廣播消息與個人消息
var stompClient = null; var socket = null; function connect() { //從sesionStorage獲取坐席工號 下面連接頭需要 這個按照自己需求修改 agentNo = sessionStorage.getItem("agentNo"); //建立連接對象(還未發起連接) 地址更換成自己的 連接服務器注冊端點endPoint時,寫訪問服務器的全路徑URL: socket = new SockJS('地址'); //獲取 STOMP 子協議的客戶端對象 stompClient = Stomp.over(socket); //建立連接,並且連接頭上定義了坐席工號 stompClient.connect({ userName: agentNo // <---這是頭 在后面的監聽 連接成功的類可以獲取到 業務代碼需要 按需刪除 }, function (frame) { console.log('Connected: ' + frame); //訂閱廣播消息 stompClient.subscribe('/topic/greetings', function (greeting) { var data = JSON.parse(greeting.body).data; //實現自己的需求 }); //訂閱個人消息 stompClient.subscribe('/user/' + agentNo + '/message', function (greeting) { var data = JSON.parse(greeting.body); //實現自己的需求 }); }); }
2.發送消息
function send(data) { var param = { id: data, release: true } stompClient.send("/app/release", {}, JSON.stringify(param)); // <----- app 就是在config中定義的客戶端往服務器發送的前綴,param是信息.必須用實體發送后台必須用實體接收 //release是后面controller中定義的 相當於RequestMapping中寫的地址一樣 }
四.實現后端代碼
1.發送廣播
@Access(name = "發布公告") @MessageMapping("/release") @SendTo("/topic/greetings") public WebResult release(@RequestBody Announcement announcement) { //執行業務代碼邏輯 }
2.發送個人消息
@Autowired private SimpMessagingTemplate messagingTemplate; public void demo() { // messagingTemplate.convertAndSend(url,""); //方法1 //方法2 相當於/user/' + agentNo + '/message' 對應前端訂閱的地址 messagingTemplate.convertAndSendToUser(ledgerQuery.getAgentNo(), "/message", ""); }