WebSocket實現前后端通訊


WebSocket實現前后端通訊

 

      長安如夢里,何日是歸期。

 

簡介:我們上線了一個商城項目,移交運營團隊使用之后,他們要求商城有新訂單來的時候同時加上聲音提示,讓她們可以及時知道有單來了。我這邊想了想,這個需求是在后端完成還是前端完成,但是仔細一想,無論是在前端還是后端完成都一樣,需求注定甩不出去了,因為這個商城的后台管理沒有前端工程師,前后端的工作都是一個后端工程師來完成的。這也導致前端界面很難看,包括前端代碼風格~因為這是我第一次寫這么多前端代碼的項目~哈哈~敢讓我寫~我就敢寫。

一、思路:

1、平台實現推送,之在前的項目有用過Ajax輪詢的技術,這種方式瀏覽器需要不斷的向服務器發出請求,會浪費很多的帶寬等資源,技術可行但不太好。

2、完完全全在前端實現此需求,在前端監聽訂單列表中元素的變化,循環遍歷訂單列表監聽或使用Vue的Watch監聽,當訂單列表有新增元素即可調用播放音效API,感覺不怎么靠譜,沒去試過。

3、最終使用了Websocket實現的 ,WebSocket是HTML5開始提供的一種在單個TCP連接上進行全雙工通訊的協議,能更好的節省服務器資源和帶寬,並且能夠更實時地進行通訊。WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據,在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接-長連接,並進行雙向數據傳輸。我猜使用這個方案最重要的原因是后端開發是我更擅長的領域吧。

二、SpringBoot/SpringCloud整合WebSocket

1、引入WebSocket Jar包

在需要用到WebSocket 通信的SpringBoot 工程的pom.xml 文件中引入WebSocket Jar包。

1       <!-- test websocket -->
2         <dependency>
3             <groupId>org.springframework.boot</groupId>
4             <artifactId>spring-boot-starter-websocket</artifactId>
5         </dependency>    

2、創建WebSocket 配置類

該配置類用於檢測帶注解@ServerEndpoint 的bean 並將它們注冊到容器中。

 1 package com.tjt.mall.config;  2 
 3 import lombok.extern.slf4j.Slf4j;  4 import org.springframework.context.annotation.Bean;  5 import org.springframework.context.annotation.Configuration;  6 import org.springframework.web.socket.server.standard.ServerEndpointExporter;  7 
 8 
 9 @Configuration 10 @Slf4j 11 public class WebSocketConfig { 12     /**
13  * 給spring容器注入這個ServerEndpointExporter對象 14  * 相當於xml: 15  * <beans> 16  * <bean id="serverEndpointExporter" class="org.springframework.web.socket.server.standard.ServerEndpointExporter"/> 17  * </beans> 18  * <p> 19  * 檢測所有帶有@serverEndpoint注解的bean並注冊他們。 20  * 21  * @return
22      */
23  @Bean 24     public ServerEndpointExporter serverEndpointExporter() { 25         log.info("serverEndpointExporter被注入了"); 26         return new ServerEndpointExporter(); 27  } 28 }
View Code

3、創建WebSocket 處理類

該WebSocket 處理類需要使用@ServerEndpoint 注解,在這個類里監聽連接的建立、關閉和消息的接收等。

 1 package com..mall.config;  2 
 3 import org.slf4j.Logger;  4 import org.slf4j.LoggerFactory;  5 import org.springframework.stereotype.Component;  6 
 7 import javax.annotation.PostConstruct;  8 import javax.websocket.*;  9 import javax.websocket.server.ServerEndpoint;  10 import java.io.IOException;  11 import java.util.concurrent.CopyOnWriteArraySet;  12 import java.util.concurrent.atomic.AtomicInteger;  13 
 14 @ServerEndpoint(value = "/ws/path/asset")    // WebSocket 路徑
 15 @Component  16 public class WebSocketServer {  17 
 18     private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);  19     private static final AtomicInteger OnlineCount = new AtomicInteger(0);  20     // concurrent包的線程安全Set,用來存放每個客戶端對應的Session對象。
 21     private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();  22 
 23  @PostConstruct  24     public void init() {  25         log.info("websocket 加載");  26  }  27 
 28 
 29     /**
 30  * 連接建立成功調用的方法  31      */
 32  @OnOpen  33     public void onOpen(Session session) throws IOException{  34  SessionSet.add(session);  35         int cnt = OnlineCount.incrementAndGet(); // 在線數加1
 36         log.info("有連接加入,當前連接數為:{}", cnt);  37        // SendMessage(session, "連接成功");
 38  }  39 
 40     /**
 41  * 連接關閉調用的方法  42      */
 43  @OnClose  44     public void onClose(Session session) {  45  SessionSet.remove(session);  46         int cnt = OnlineCount.decrementAndGet();  47         log.info("有連接關閉,當前連接數為:{}", cnt);  48  }  49 
 50     /**
 51  * 收到客戶端消息后調用的方法  52  *  53  * @param message  54  * 客戶端發送過來的消息  55      */
 56  @OnMessage  57     public void onMessage(String message, Session session) throws IOException {  58         log.info("來自客戶端的消息:{}",message);  59         SendMessage(session, "收到消息,消息內容:"+message);  60 
 61  }  62 
 63     /**
 64  * 出現錯誤  65  * @param session  66  * @param error  67      */
 68  @OnError  69     public void onError(Session session, Throwable error) {  70         log.error("發生錯誤:{},Session ID: {}",error.getMessage(),session.getId());  71  error.printStackTrace();  72  }  73 
 74     /**
 75  * 發送消息,實踐表明,每次瀏覽器刷新,session會發生變化。  76  * @param session  77  * @param message  78      */
 79     public static void SendMessage(Session session, String message) throws IOException {  80         try {  81 // session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
 82  session.getBasicRemote().sendText(message);  83         } catch (IOException e) {  84             log.error("發送消息出錯:{}", e.getMessage());  85  e.printStackTrace();  86  }  87  }  88 
 89     /**
 90  * 群發消息  91  * @param message  92  * @throws IOException  93      */
 94     public static void BroadCastInfo(String message) throws IOException {  95         for (Session session : SessionSet) {  96             if(session.isOpen()){  97  SendMessage(session, message);  98  }  99  } 100  } 101 
102     /**
103  * 指定Session發送消息 104  * @param sessionId 105  * @param message 106  * @throws IOException 107      */
108     public static void SendMessage(String message,String sessionId) throws IOException { 109         Session session = null; 110         for (Session s : SessionSet) { 111             if(s.getId().equals(sessionId)){ 112                 session = s; 113                 break; 114  } 115  } 116         if(session!=null){ 117  SendMessage(session, message); 118  } 119         else{ 120             log.warn("沒有找到你指定ID的會話:{}",sessionId); 121  } 122  } 123 }
View Code

4、調用WebSocket 發送消息

在訂單生成的代碼后,根據需求調用WebSocket 處理類中的群發或者單獨發送消息的方法。

1         log.info("購物車生成訂單OK: {}", result); 2         // send webSocket message
3         WebSocketServer.BroadCastInfo("after service order");        

三、VUE整合WebSocket

在前端使用WebSocket 時還需要判斷下,因為目前雖然大部分瀏覽器都支持WebSocket,比如Chrome、Mozilla、Opera 和Safari,但還有少部分是不支持的。

1、HTML中操作WebSocket

在html頁面中建立websocket 連接,至於什么時候連接WebSocket,看個人需求吧。我是在登錄成功后,也就是在登錄頁面的Html中連接WebSocket 並監聽消息。

我把登錄HTML頁面中的部分代碼刪除了,只留下WebSocket 相關操作的和播放音效相關的代碼。

 1 <template>
 2   <div class="dashboard-container">
 3   <!-- 在div 中引入 音頻資源-->
 4     <audio ref="audio" src="@/assets/voice/tjtVoice.mp3"></audio>
 5   </div>
 6 </template>
 7 
 8 <script>
 9 
10 export default { 11 
12 created() { 13     this.playVoice() 14  }, 15 
16  methods: { 17  playVoice() { 18       var socket; 19       // 當找不到句柄對象即音頻資源的時候使用 that
20  let that = this
21       if (typeof (WebSocket) == "undefined") { 22  console.log("遺憾:您的瀏覽器不支持WebSocket"); 23  } else { 24  console.log("恭喜:您的瀏覽器支持WebSocket"); 25         //實現化WebSocket對象
26         //指定要連接的服務器地址與端口建立連接
27         //注意ws、wss使用不同的端口。我使用自簽名的證書測試,
28         //無法使用wss,瀏覽器打開WebSocket時報錯
29         //ws對應http、wss對應https。
30  socket = new WebSocket("ws://localhost:11200/ws/path/asset"); 31         //連接打開事件
32  socket.onopen = function() { 33  console.log("Socket 已打開"); 34           // socket.send("消息發送測試(From Client)");
35 
36  }; 37         //收到消息事件
38  socket.onmessage = msg => { 39           // 當收到消息調用播放音頻資源API
40  console.log(msg.data); 41            this.$refs.audio.play() 42  }; 43         //連接關閉事件
44  socket.onclose = function() { 45  console.log("Socket已關閉"); 46  }; 47         //發生了錯誤事件
48  socket.onerror = function() { 49  alert("Socket發生了錯誤"); 50  } 51         //窗口關閉時,關閉連接
52  window.unload=function() { 53          // socket.close();
54  }; 55  } 56  } 57  } 58 
59 } 60 </script>
View Code

四、測試效果

1、啟動裝配有WebSocket 的SpringBoot 工程。

2、啟動VUE 工程,點擊登錄,連接WebSocket。

后端工程控制台打印

[2021-07-07 08:45:47] [INFO ] -- 有連接加入,當前連接數為:1

前端瀏覽器的WS 下分別顯示WebSocket 連接狀態:

3、模擬訂單生成,在后台調用訂單生成接口API 。

前端瀏覽器Console 打印,成功監聽並節接收到后端發送的WebSocket 訊息。

 4、音頻試聽

接收到后端發送的WebSocket 訊息,即刻播放音效。

 四、音頻資源

下載鏈接:https://pan.baidu.com/s/1rqa6Uift3RpWShPytgBLgQ 

密碼:  8arh

 

 

 

 

    長安如夢里

何日是歸期 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM