WebSocket實現前後端通訊

濤姐濤哥發表於2021-07-07

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 並將它們註冊到容器中。

WebSocket實現前後端通訊
 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 註解,在這個類裡監聽連線的建立、關閉和訊息的接收等。

WebSocket實現前後端通訊
  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 相關操作的和播放音效相關的程式碼。

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

 

 

 

 

    長安如夢裡

何日是歸期 

 

 

 

相關文章