一、導入依賴
分別導入websocket依賴包和處理json格式數據的fastjson依賴包。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency>
二、註解處理
以下註解可以將配置爲@ServerEndpoint標註的類在調用時自動導出爲實體,類編寫完成後使用@Configuration將config加入配置。
package com.example.demo2.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
三、websocket類編寫
package com.example.demo2.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.util.JSONPObject; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; @Component @ServerEndpoint("/websocket/{userId}") public class MessageController { //與某個客戶端的連接會話,需要用來發送數據 private Session session; //concurrent線程安全set,存放每個客戶端對應的websocket對象 private static CopyOnWriteArraySet<MessageController> messageControllers = new CopyOnWriteArraySet<>(); //存儲session池 private static Map<String,Session> sessionPool = new HashMap<String,Session>(); /** * 鏈接成功調用 */ @OnOpen public void onOpen(Session session, @PathParam(value = "userId")String userId){ try{ //session獲取 this.session = session; //靜態存儲的websocket長鏈接集合中,添加本次調用成功所創建的這個實體 messageControllers.add(this); //session緩存池添加用戶的用戶ID和對應的session sessionPool.put(userId,session); //將用戶加入鏈接和當前連接數的消息廣播給當前所有已連接的用戶 sendAllMessage("【" + userId +"】加入鏈接,當前連接數:" + messageControllers.size()); }catch (Exception e){ e.printStackTrace(); } } /** * 鏈接關閉調用 */ @OnClose public void onClose(){ try{ //已連接的對象列表中移除當前調用此關閉方法的對象 messageControllers.remove(this); //將用戶斷開鏈接和鏈接總數信息推送給現在還鏈接着的用戶 System.out.println("用戶斷開鏈接,當前鏈接總數爲:" + messageControllers.size()); }catch (Exception e){ } } /** * 收到客戶端消息 */ @OnMessage public void onMessage(String message){ //收到客戶端消息後可以根據實際需求做一些處理操作,也可以直接返回,到頁面進行處理 //廣播給羣中現在處理已連接狀態的所有用戶 sendAllMessage(message); } /** * 發送錯誤處理 */ @OnError public void onError(Session session,Throwable error){ //輸出錯誤信息 System.out.println("【發送錯誤】:" + error.getMessage()); error.printStackTrace(); } /** * 廣播(全體)消息 */ public void sendAllMessage(String message){ //遍歷當前所有已連接的用戶,循環發送 for(MessageController messageController : messageControllers){ try{ //session處於打開狀態,則發送消息 if(messageController.session.isOpen()){ //最好是使用這個getAsyncRemote,另一個(getBasicRemote)在多個線程同時進行時容易報錯,這兩個是異步和同步的區別 messageController.session.getAsyncRemote().sendText(message); } }catch (Exception e){ e.printStackTrace(); } } } /** * 單點消息 */ public void sendOneMessage(String userId,String message){ //根據userId獲取到對應的session Session session = sessionPool.get(userId); //這個session不爲空並且還在開放狀態 if(session != null && session.isOpen()){ try{ //將消息單獨推送到這個userId對應的session裏 session.getAsyncRemote().sendText(message); }catch (Exception e){ e.printStackTrace(); } } } /** * 多點消息(多選發給一部分人) */ public void sendManyMessage(String[] userIds,String message){ //遍歷參數中的用戶id數組,進行多個推送,介於單個和廣播(全體)之間的部分發送 for(String userId:userIds){ Session session = sessionPool.get(userId); if(session != null && session.isOpen()){ try{ session.getAsyncRemote().sendText(message); }catch (Exception e){ e.printStackTrace(); } } } } }
除了在這個類內部調用發送信息的方法之外,其他可以通過類中注入此類後,使用接口進行方法調用。(之前本來想用main方法測試一下發現這種方式不行,還是要在接口中調用)
四、websocket在vue頁面裏進行應用
<template> <div> <!--填寫個人信息 --> <input v-model="userId" /> <input v-model="userName" /> <!-- 綁定後根據個人信息創建websocket連接 --> <button @click="andUser">綁定</button> <br /> <!-- 填寫要發送的消息 --> <input v-model="message" /> <!-- 發送信息 --> <button @click="webSocketSend">發送</button> <br /> <!-- 遍歷顯示所有信息列表 --> <div v-for="item in infoList"> <span>{{ item }}</span> <br /> </div> </div> </template> <script> export default { name: "Index", data(){ return{ //默認用戶信息 userId:1, userName:"張三", //預置websocket,設爲undefined websock:undefined, //要發送的消息 message:"", //消息列表 infoList:[], } }, mounted() { //如果多地進行測試,可以在這裏進行初始化 //一個人開多個窗口的話,可以在填寫不同用戶信息後,點擊綁定時使用不同的userId進行初始化 }, destroyed() { //頁面銷燬時觸發,關閉此用戶的websocket連接 this.webSocketOnClose(); }, methods:{ //建立連接 initWebSocket(){ //websocket服務鏈接的地址 let url = "ws://127.0.0.1:8080/websocket/" + this.userId; //創建連接 this.websock = new WebSocket(url); //將自定義的方法替換websocket自帶的方法 this.websock.onopen = this.webSocketOnOpen; // this.websock.send = this.webSocketSend; this.websock.onerror = this.webSocketOnError; this.websock.onmessage = this.webSocketOnMessage; this.websock.onclose = this.webSocketOnClose; }, //連接上之後觸發 webSocketOnOpen(){ //將用戶連接成功的消息放到列表進行展示 this.infoList.push("【" + this.userName + "】連接成功"); }, //發送消息時觸發 webSocketSend(){ //將用戶信息和要發送的消息包裝爲json let data = { userId:this.userId, userName:this.userName, message:this.message } //將json格式化爲字符串之後發送到服務端 this.websock.send(JSON.stringify(data)); //自己發送的信息添加到列表進行展示 this.infoList.push(this.userName + ":" + this.message); }, //發送錯誤時觸發 webSocketOnError(e){ //將錯誤信息添加到列表展示在頁面 this.infoList.push("【錯誤】:" + e.toString()); }, //接收消息時觸發 webSocketOnMessage(e){ //返回的信息存儲在此方法參數的data字段 if(e.data != undefined && e.data != null && e.data != ''){ //信息非空時,通過json格式化爲表單 let json = JSON.parse(e.data); //如果返回消息的用戶id是本人,則爲自己發送的信息,不需要顯示 if(json.userId != this.userId){ //判斷是別人發送的信息,則拼接字段並添加到列表中用於展示 this.infoList.push(json.userName + ":" + json.message); } } }, //關閉連接時觸發 webSocketOnClose(){ //將用戶關閉鏈接的消息寫入列表 this.infoList.push("【" + this.userName + "】關閉連接"); }, //綁定用戶 andUser(){ //初始化websocket this.initWebSocket(); } } } </script> <style scoped> </style>