感謝作者,支持原創: https://blog.csdn.net/moshowgame/article/details/80275084
什么是WebSocket?
WebSocket協議是基於TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)通信——允許服務器主動發送信息給客戶端。
為什么需要 WebSocket?
初次接觸 WebSocket 的人,都會問同樣的問題:我們已經有了 HTTP 協議,為什么還需要另一個協議?它能帶來什么好處?
- 答案很簡單,因為 HTTP 協議有一個缺陷:通信只能由客戶端發起,HTTP 協議做不到服務器主動向客戶端推送信息。
舉例來說,我們想要查詢當前的排隊情況,只能是頁面輪詢向服務器發出請求,服務器返回查詢結果。輪詢的效率低,非常浪費資源(因為必須不停連接,或者 HTTP 連接始終打開)。因此WebSocket 就是這樣發明的。
Maven依賴
1 <!--springboot的高級組件會自動引用基礎的組件--> 2 <!--,像spring-boot-starter-websocket就引入了spring-boot-starter-web和spring-boot-starter,--> 3 <!--所以不要重復引入。--> 4 <!--JavaEE標准--> 5 <dependency> 6 <groupId>javax</groupId> 7 <artifactId>javaee-api</artifactId> 8 <scope>provided</scope> 9 </dependency> 10 <!--websocket--> 11 <dependency> 12 <groupId>org.springframework.boot</groupId> 13 <artifactId>spring-boot-starter-websocket</artifactId> 14 </dependency>
WebSocket配置類
1 package com.zr.demo.config; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.web.socket.server.standard.ServerEndpointExporter; 6 7 /** 8 * @Author: Hujh 9 * @Date: 2019/7/16 11:10 10 * @Description: WebSocket配置類 11 */ 12 @Configuration 13 public class WebSocketConfig { 14 15 /*使用@ServerEndpoint創立websocket endpoint*/ 16 @Bean 17 public ServerEndpointExporter serverEndpointExporter() { 18 return new ServerEndpointExporter(); 19 } 20 }
WebSocketServer
因為WebSocket是類似客戶端服務端的形式(采用ws協議),那么這里的WebSocketServer其實就相當於一個ws協議的Controller直接@ServerEndpoint("/websocket")@Component啟用即可,然后在里面實現@OnOpen,@onClose,@onMessage等方法
1 package com.zr.demo.socket; 2 3 import org.springframework.stereotype.Component; 4 5 import javax.websocket.OnClose; 6 import javax.websocket.OnMessage; 7 import javax.websocket.OnOpen; 8 import javax.websocket.Session; 9 import javax.websocket.server.ServerEndpoint; 10 import java.io.IOException; 11 import java.util.concurrent.CopyOnWriteArraySet; 12 13 /** 14 * @Author: Hujh 15 * @Date: 2019/7/16 11:13 16 * @Description: webSocket實現類 17 */ 18 @ServerEndpoint(value = "/webSocket") 19 @Component 20 public class WebSocket { 21 //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。 22 private static int onlineCount = 0; 23 24 //concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。 25 private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>(); 26 27 //與某個客戶端的連接會話,需要通過它來給客戶端發送數據 28 private Session session; 29 30 /** 31 * 連接建立成功調用的方法 32 */ 33 @OnOpen 34 public void onOpen(Session session) { 35 this.session = session; //加入set中 36 webSocketSet.add(this); 37 addOnlineCount(); //在線數加1 38 System.out.println("有新連接加入!當前在線人數為:" + getOnlineCount()); 39 try { 40 sendMessage("連接成功"); 41 } catch (IOException e) { 42 System.out.println("IO異常"); 43 } 44 } 45 46 /** 47 * 連接關閉調用的方法 48 */ 49 @OnClose 50 public void onClose() { 51 webSocketSet.remove(this); //從set中刪除 52 subOnlineCount(); //在線數減1 53 System.out.println("有一連接關閉!當前在線人數為" + getOnlineCount()); 54 } 55 56 /** 57 * 收到客戶端消息后調用的方法 58 * @param message 客戶端發送過來的消息 59 */ 60 @OnMessage 61 public void onMessage(String message, Session session) { 62 System.out.println("來自客戶端的消息:" + message); 63 64 //群發消息 65 for (WebSocket item : webSocketSet) { 66 try { 67 item.sendMessage(message); 68 } catch (IOException e) { 69 e.printStackTrace(); 70 } 71 } 72 } 73 74 /** 75 * 發生錯誤時調用 76 @OnError 77 */ 78 public void onError(Session session, Throwable error) { 79 System.out.println("發生錯誤"); 80 error.printStackTrace(); 81 } 82 83 /* 84 * @Title: sendMessage 85 * @Author : Hujh 86 * @Date: 2019/7/17 10:52 87 * @Description : 發送消息 88 * @param : message 89 * @Return : void 90 */ 91 public void sendMessage(String message) throws IOException { 92 this.session.getBasicRemote().sendText(message); 93 //this.session.getAsyncRemote().sendText(message); 94 } 95 96 97 /* 98 * @Title: sendInfo 99 * @Author : Hujh 100 * @Date: 2019/7/17 10:52 101 * @Description : 群發自定義消息 102 * @param : message 103 * @Return : void 104 */ 105 public static void sendInfo(String message) throws IOException { 106 for (WebSocket item : webSocketSet) { 107 try { 108 item.sendMessage(message); 109 } catch (IOException e) { 110 continue; 111 } 112 } 113 } 114 115 /* 116 * @Title: getOnlineCount 117 * @Author : Hujh 118 * @Date: 2019/7/17 10:51 119 * @Description : 獲得當前在線人數 120 * @param : 121 * @Return : int 122 */ 123 public static synchronized int getOnlineCount() { 124 return onlineCount; 125 } 126 127 /* 128 * @Title: addOnlineCount 129 * @Author : Hujh 130 * @Date: 2019/7/17 10:51 131 * @Description : 在線人數+1 132 * @param : 133 * @Return : void 134 */ 135 public static synchronized void addOnlineCount() { 136 WebSocket.onlineCount++; 137 } 138 139 /* 140 * @Title: subOnlineCount 141 * @Author : Hujh 142 * @Date: 2019/7/17 10:52 143 * @Description :在線人數-1 144 * @param : 145 * @Return : void 146 */ 147 public static synchronized void subOnlineCount() { 148 WebSocket.onlineCount--; 149 } 150 151 }
消息推送
至於推送新信息,可以再自己的Controller寫個方法調用WebSocket.sendInfo();
1 package com.zr.demo.controller; 2 3 import com.zr.demo.socket.WebSocket; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RequestParam; 7 import org.springframework.web.bind.annotation.RestController; 8 9 /** 10 * @Author: Hujh 11 * @Date: 2019/7/19 15:40 12 * @Description: 消息發送控制器 13 */ 14 @RestController 15 public class SendMessageController { 16 17 @Autowired 18 private WebSocket webSocket; 19 20 @RequestMapping("sendInfo") 21 public String sendInfo(@RequestParam String msg) { 22 try { 23 webSocket.sendInfo(msg); 24 } catch (Exception e) { 25 e.printStackTrace(); 26 return "信息發送異常!"; 27 } 28 29 return "發送成功~"; 30 } 31 32 }
頁面代碼
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>WebSocket</title> 5 </head> 6 7 <body> 8 <div style="width: 800px;height: 100%; margin: 0px auto;"> 9 <span style="color: coral; font-size: 22px;"> Welcome WebSocket</span><br/><br/> 10 <div id="message"> 11 </div> 12 </div> 13 </body> 14 15 <script type="text/javascript"> 16 var websocket = null; 17 //判斷當前瀏覽器是否支持WebSocket 18 if('WebSocket' in window){ 19 websocket = new WebSocket("ws://localhost:8082/webSocket"); 20 }else{ 21 alert('連接失敗!!') 22 } 23 24 //連接發生錯誤的回調方法 25 websocket.onerror = function(){ 26 setMessageInnerHTML("error"); 27 }; 28 29 //連接成功建立的回調方法 30 websocket.onopen = function(event){ 31 setMessageInnerHTML("webSocket 連接成功~"); 32 } 33 34 //接收到消息的回調方法 35 websocket.onmessage = function(event){ 36 setMessageInnerHTML(event.data); 37 } 38 39 //連接關閉的回調方法 40 websocket.onclose = function(){ 41 setMessageInnerHTML("close"); 42 } 43 44 //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接, 45 // 防止連接還沒斷開就關閉窗口,server端會拋異常。 46 window.onbeforeunload = function(){ 47 websocket.close(); 48 } 49 50 //將消息顯示在網頁上 51 function setMessageInnerHTML(innerHTML){ 52 document.getElementById('message').innerHTML += innerHTML + '<br/>'; 53 } 54 55 //關閉連接 56 function closeWebSocket(){ 57 websocket.close(); 58 } 59 60 61 </script> 62 </html>
測試效果
1.建立兩個連接
2.消息推送
注:本文僅作為個人學習記錄,不提供任何參考價值!