本文章包括websocket面試相關問題以及spring boot如何整合webSocket。
參考文檔 https://blog.csdn.net/prayallforyou/article/details/53737901 、https://www.cnblogs.com/bianzy/p/5822426.html
webSocket是HTML5的一種新協議,它實現了服務端與客戶端的全雙工通信,建立在傳輸層,tcp協議之上,即瀏覽器與服務端需要先建立tcp協議,再發送webSocket連接建立請求。
webSocket的連接:客戶端發送請求信息,服務端接受到請求並返回相應的信息。連接建立。客戶端發送http請求時,通過 Upgrade:webSocket Connection:Upgrade 告知服務器需要建立的是webSocket連接,並且還會傳遞webSocket版本號,協議的字版本號,原始地址,主機地址等等。
webSocket相互通信的Header很小,大概只有2Bytes。
以下是基於spring boot及支持webScoket的高版本瀏覽器的配置過程。
一、pom.xml中引入webSocket組件
</dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> </dependencies>
二、后台引入webSocket
要想讓一個類處理webScoket的請求,需要兩個東西:
① 類名上加webScoket請求攔截注釋@ServerEndpoint(value="/***")
② 類需要繼承org.springframework.web.socket.server.standard.ServerEndpointExporter,重寫交互過程中各種情況下調用的方法(建立時、斷開時、出錯時、接收消息、發送消息)
針對②,一方面根據spring的IOC特性,需要反向代理,另一方面因為webSocket是一個功能而不僅僅是屬於某個業務,所以應當在配置文件中聲明。配置文件可以是xml文件,也可以是注釋了@Configuration的類文件,根據個人喜好使用~,這里使用的是@Configuration。
@Configuration public class ProjectConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
使用@ServerEndpoint(value="/***") 時會自動注入返回類型為ServerEndpointExporter的bean。等同於繼承了ServerEndpointExporter類。繼承類后再重寫onOpen、onClose、onMessage、onError方法,因為不是直接使用繼承,所以方法的重寫也需要使用注釋,代碼如下
package com.example.SpringBootTry.controller.webSocket; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; /** * 雙工通信websocket工具類 * @author wwl * */ @ServerEndpoint(value="/webSocket") @Component public class WebSocketUtil{ //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。 private static int onlineCount = 0; //concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。 private static CopyOnWriteArraySet<WebSocketUtil> webSocketSet = new CopyOnWriteArraySet<WebSocketUtil>(); //與某個客戶端的連接會話,需要通過它來給客戶端發送數據 private Session session; /** * 連接建立成功調用的方法*/ @OnOpen public void onOpen(Session session) { this.session = session; webSocketSet.add(this); //加入set中 addOnlineCount(); //在線數加1 System.out.println("有新連接加入!當前在線人數為" + getOnlineCount()); try { sendMessage("您是第" + getOnlineCount() + "個雙工通信的用戶!"); } catch (IOException e) { System.out.println("IO異常"); } } /** * 連接關閉調用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); //從set中刪除 subOnlineCount(); //在線數減1 System.out.println("有一連接關閉!當前在線人數為" + getOnlineCount()); } /** * 收到客戶端消息后調用的方法 * * @param message 客戶端發送過來的消息*/ @OnMessage public void onMessage(String message, Session session) { System.out.println("來自客戶端的消息:" + message); //發送消息 try { session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } } /** * 發生錯誤時調用 */ @OnError public void onError(Session session, Throwable error) { System.out.println("發生錯誤"); error.printStackTrace(); } /** * 發送消息 * @param message * @throws IOException */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message); } /** * 群發自定義消息 * */ public static void sendInfo(String message) throws IOException { for (WebSocketUtil item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketUtil.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketUtil.onlineCount--; } }
三、前端引入webSocket
使用 var websocket = new WebSocket("ws://localhost:8081/***") 建立webSocket連接,定義websocket的onerror、onopen、onmessage、onclose的屬性,跟后台的四個方法相對應,完成合理的webSocket交互。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>websocket測試頁面</title> <meta http-equiv="keywords" content="websocket,例子"> <meta http-equiv="description" content="測試websocket"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css">--> </head> <body> Welcome<br/> <input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button> <div id="message"> </div> </body> <script type="text/javascript"> var websocket = null; //判斷當前瀏覽器是否支持WebSocket if('WebSocket' in window){ websocket = new WebSocket("ws://localhost:8081/webSocket"); }else{ alert('Not support websocket') } //連接發生錯誤的回調方法 websocket.onerror = function(){ setMessageInnerHTML("error"); }; //連接成功建立的回調方法 websocket.onopen = function(event){ setMessageInnerHTML("open"); } //接收到消息的回調方法 websocket.onmessage = function(event){ setMessageInnerHTML(event.data); } //連接關閉的回調方法 websocket.onclose = function(){ setMessageInnerHTML("close"); } //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。 window.onbeforeunload = function(){ websocket.close(); } //將消息顯示在網頁上 function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //關閉連接 function closeWebSocket(){ websocket.close(); } //發送消息 function send(){ var message = document.getElementById('text').value; websocket.send(message); } </script> </html>
以上為spring boot 整合webSocket的一些入門知識。有錯誤歡迎指正。
demo地址:https://github.com/ttjsndx/someDemo/blob/master/SpringBootTryDemo.rar