springboot - websocket實現及原理

本文章包括websocket面試相關問題以及spring boot如何整合webSocket。javascript

參考文檔 https://blog.csdn.net/prayallforyou/article/details/53737901 、https://www.cnblogs.com/bianzy/p/5822426.htmlcss

  webSocket是HTML5的一種新協議,它實現了服務端與客戶端的全雙工通訊,創建在傳輸層,tcp協議之上,即瀏覽器與服務端須要先創建tcp協議,再發送webSocket鏈接創建請求。html

  webSocket的鏈接:客戶端發送請求信息,服務端接受到請求並返回相應的信息。鏈接創建。客戶端發送http請求時,經過  Upgrade:webSocket Connection:Upgrade 告知服務器須要創建的是webSocket鏈接,而且還會傳遞webSocket版本號,協議的字版本號,原始地址,主機地址等等。前端

  webSocket相互通訊的Header很小,大概只有2Bytes。java


  如下是基於spring boot及支持webScoket的高版本瀏覽器的配置過程。git

1、pom.xml中引入webSocket組件github

</dependencies>
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-websocket</artifactId>
     </dependency>
</dependencies>

2、後臺引入webSocketweb

  要想讓一個類處理webScoket的請求,須要兩個東西:面試

  ①  類名上加webScoket請求攔截註釋@ServerEndpoint(value="/***")spring

    類須要繼承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--; } }

 3、前端引入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

相關文章
相關標籤/搜索