SpringBoot中使用WebSocket的教程分享
為什么要用WebSocket
我們往往需要一些這樣的場景,服務器給客戶端推送消息
,如淘寶推送消息,網(wǎng)上聊天等,這些場景下客戶端沒有主動向服務器發(fā)請求,而是由服務器主動的向客戶端發(fā)送消息,但是之前用的HTTP協(xié)議是一次請求一次響應
那該如何實現(xiàn)服務器主動的向客戶端推送消息呢?
如果繼續(xù)使用HTTP協(xié)議,可以基于輪詢
的方式實現(xiàn),也就是客戶端每隔一段時間給服務器發(fā)請求,看看有沒有要發(fā)送給我的消息,如果有就獲取到消息,如果沒有就等待
上述輪詢存在一定問題:
- 消耗更多的系統(tǒng)資源,客戶端要頻繁的向服務器發(fā)請求,而這些請求大多數(shù)是沒有響應的
- 獲取消息不夠及時,只有輪詢的時候(下次請求的周期)才能夠獲取到消息
如果提高輪詢頻率,則將消耗更多的系統(tǒng)資源,如果降低輪詢頻率,那獲取消息就不夠及時
此時可以使用WebSocket
協(xié)議,WebSocket協(xié)議也是應用層協(xié)議,傳輸層也是基于TCP協(xié)議的,該協(xié)議可以實現(xiàn)服務器主動向客戶端推送消息的功能
WebSocket的握手階段
先了解一下報文格式中的幾個重要信息:
- FIN:表示是否關(guān)閉websocket
- opcode操作碼:描述了當前的websocket數(shù)據(jù)幀是起到了啥作用(0x1表示文本數(shù)據(jù),0x2表示二進制數(shù)據(jù))
- MASK:是否開啟掩碼操作,掩碼操作是為了避免緩沖區(qū)溢出
- payload length:載荷的長度
- payload data:載荷真正攜帶的數(shù)據(jù)
WebSocket協(xié)議的握手過程
總結(jié)如下:
- 客戶端向服務端發(fā)一個申請建立websocket連接的HTTP請求,該請求是基于HTTP協(xié)議的,這個HTTP請求的請求頭包含了重要的Header頭,如Connection: upgrade,Upgrade: websocket,標識要進行協(xié)議升級,并升級的協(xié)議類型為websocket
- 服務端收到該請求后,返回一個HTTP響應,響應狀態(tài)碼為101表示協(xié)議切換,并且響應也會包含重要的Header頭Connection: upgrade,Upgrade: websocket
- 客戶端與服務端建立好全雙工的websocket長連接,后續(xù)傳輸都是基于WebSocket協(xié)議
Spring Boot中使用WebSocket
添加WebSocket依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
服務器代碼編寫
主要的步驟分為以下兩步:
- 創(chuàng)建一個類作為WebSocketHandler(處理WebSocket中各個通信流程)
- 把上述類注冊到Spring中,配置路由(關(guān)聯(lián)上哪個路徑對應上述的handler)
創(chuàng)建一個類繼承TextWebSocketHandler,并添加類注解@Component將該類注冊到Spring中,并重寫:
- afterConnectionEstablished:該方法會在websocket連接成功后被調(diào)用
- handleTextMessage:該方法是在websocket收到消息的時候自動調(diào)用
- handleTransportError:該方法是在websocket連接出現(xiàn)異常的時候自動調(diào)用的
- afterConnectionClosed:該方法是在websocket連接關(guān)閉后自動調(diào)用的
@Component public class WebSocketAPI extends TextWebSocketHandler { @Override //該方法會在websocket連接成功后被調(diào)用 public void afterConnectionEstablished(WebSocketSession session) throws Exception { //WebSocketSession是websocket連接對應的會話 System.out.println("建立連接了"); } @Override //該方法是在websocket收到消息的時候自動調(diào)用 protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { //message 為收到的消息 System.out.println("發(fā)送消息:"+message.toString()); //session是個會話,里面記錄了通信雙方,通過session對象調(diào)用send方法實現(xiàn)服務器推送消息 session.sendMessage(message); message.getPayload(); //獲取的message字符串 } @Override //該方法是在websocket連接出現(xiàn)異常的時候自動調(diào)用的 public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { //exception記錄了異常信息 System.out.println("連接出現(xiàn)異常了"); } @Override //該方法是在websocket連接關(guān)閉后自動調(diào)用的 public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { //status 為關(guān)閉的狀態(tài) System.out.println("連接關(guān)閉了"); } }
創(chuàng)建另一個類實現(xiàn)WebSocketConfigurer接口
,在類上添加@Configuration
,@EnableWebSocket
,并重寫registerWebSocketHandlers
方法
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private WebSocketAPI webSocketAPI; @Override //通過該方法,把創(chuàng)建好的Handler類注冊到具體的路徑上 public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { //當瀏覽器,websocket的請求路徑是/test的時候,就會調(diào)用到webSocketTest中的方法 registry.addHandler(webSocketAPI,"/websocketMessage"); } }
每個和服務端建立websocket連接的客戶端,都會在服務器這邊有與之對應的WebSocketSession對象,服務器要想給誰發(fā)消息,就必須使用誰的WebSocketSession對象調(diào)用sendMessage方法發(fā)送消息,服務器向客戶端推送消息使用session.sendMessage(String message)
發(fā)送消息,session為每個服務器與客戶端建立的websocket會話WebSocketSession
WebSocketSession如何獲取用戶信息
我們通常需要獲取websocket保存的用戶會話的用戶信息,那如何獲取到連接用戶的用戶信息呢?
通過注冊特定的HttpSession攔截器,就可以把用戶給HttpSession中添加的Attribute鍵值對,往WebSocketSession中也添加一份
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private WebSocketAPI webSocketAPI; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketAPI,"/websocketMessage"). // 通過注冊特定的HttpSession攔截器,就可以把 // 用戶給HttpSession中添加的Attribute鍵值對,往WebSocketSession中也添加一份 addInterceptors(new HttpSessionHandshakeInterceptor()); } }
添加完后,可以使用WebSocketSession對象調(diào)用getAttributes().get("user")
方法獲取用戶在HttpSession中保存的用戶信息
@Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { User user = (User)session.getAttributes().get("user"); System.out.println("建立連接"); }
創(chuàng)建管理類管理用戶與會話
可以使用HashMap來維護用戶與WebSocketSession的關(guān)系,key為用戶id,value為WebSocketSession對象,只是此時使用線程安全的集合類ConcurrentHashMap
@Component public class WebSocketSessionManage { private ConcurrentHashMap<Integer, WebSocketSession> websocketSessions = new ConcurrentHashMap<>(); //用戶連接,添加連接關(guān)系 public void addWebSocketSession(Integer userId,WebSocketSession webSocketSession){ //用戶已經(jīng)上線,防止多開 if(websocketSessions.containsKey(userId)){ return; } websocketSessions.put(userId,webSocketSession); } //用戶掉線,刪除連接關(guān)系 public void delWebSocketSession(Integer userId,WebSocketSession webSocketSession){ WebSocketSession get = websocketSessions.get(userId); //只有關(guān)系中存在自己的連接信息,才刪除 if(get == webSocketSession){ websocketSessions.remove(userId); } } //根據(jù)用戶id獲取WebSocketSession public WebSocketSession getWebSocketSession(Integer userId){ return websocketSessions.get(userId); } }
客戶端代碼
客戶端使用WebSocket的實例調(diào)用send方法即可向服務器發(fā)送消息,發(fā)送的消息一般為json字符串,所以我們可以使用websocket.send(JSON.stringfy(js))
將js對象序列換為json字符串發(fā)送
//websocket傳輸消息 //創(chuàng)建websocket實例 let websocket = new WebSocket("ws://" + location.host + "/websocketMessage"); //綁定一些函數(shù) websocket.onopen = function(){ console.log('建立連接') } websocket.onmessage = function(e){ //e.data為收到服務端推送的消息 //e.data為一個json字符串,可以使用JSON.prase(e.data)轉(zhuǎn)換為js對象 let resp = JSON.prase(e.data); console.log('收到消息:'+resp) } websocket.onerror = function(){ console.log('出現(xiàn)異常') } websocket.onclose = function(){ console.log('關(guān)閉連接') } //使用websocket實例調(diào)用send方法即可向服務器發(fā)送消息 //注意:參數(shù)為字符串,不能為js對象 //要發(fā)送json格式的數(shù)據(jù),將json對象序列化為字符串,JSON.stringfy(json) websocket.send("hehe");
到此這篇關(guān)于SpringBoot中使用WebSocket的教程分享的文章就介紹到這了,更多相關(guān)SpringBoot WebSocket內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot實現(xiàn)人臉識別與WebSocket長連接的實現(xiàn)代碼
- SpringBoot+WebSocket實現(xiàn)IM及時通訊的代碼示例
- SpringBoot+websocket實現(xiàn)消息對話功能
- SpringBoot 整合WebSocket 前端 uniapp 訪問的詳細方法
- Springboot+WebSocket+Netty實現(xiàn)在線聊天/群聊系統(tǒng)
- SpringBoot集成WebSocket的兩種方式(JDK內(nèi)置版和Spring封裝版)
- SpringBoot實現(xiàn)WebSocket全雙工通信的項目實踐
- 使用WebSocket+SpringBoot+Vue搭建簡易網(wǎng)頁聊天室的實現(xiàn)代碼
- SpringBoot整合WebSocket實現(xiàn)后端向前端發(fā)送消息的實例代碼
- Springboot+WebSocket實現(xiàn)在線聊天功能
- springboot中websocket簡單實現(xiàn)
- Spring Boot中的WebSocketMessageBrokerConfigurer接口使用
相關(guān)文章
Java 實戰(zhàn)項目錘煉之IT設(shè)備固定資產(chǎn)管理系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java+SSM+jsp+mysql+maven實現(xiàn)一個IT設(shè)備固定資產(chǎn)管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11java Swing組件setBounds()簡單用法實例分析
這篇文章主要介紹了java Swing組件setBounds()簡單用法,結(jié)合實例形式分析了Swing組件setBounds()方法的功能與簡單使用方法,需要的朋友可以參考下2017-11-11springboot layui hutool Excel導入的實現(xiàn)
本文主要介紹了springboot layui hutool Excel導入的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-03-03微信小程序與AspNetCore SignalR聊天實例代碼
這篇文章主要介紹了微信小程序與AspNetCore SignalR聊天實例代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08JavaSwing BorderLayout 邊界布局的實現(xiàn)代碼
這篇文章主要介紹了JavaSwing BorderLayout 邊界布局的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12