SpringBoot中使用WebSocket的教程分享
為什么要用WebSocket
我們往往需要一些這樣的場(chǎng)景,服務(wù)器給客戶端推送消息,如淘寶推送消息,網(wǎng)上聊天等,這些場(chǎng)景下客戶端沒(méi)有主動(dòng)向服務(wù)器發(fā)請(qǐng)求,而是由服務(wù)器主動(dòng)的向客戶端發(fā)送消息,但是之前用的HTTP協(xié)議是一次請(qǐng)求一次響應(yīng)
那該如何實(shí)現(xiàn)服務(wù)器主動(dòng)的向客戶端推送消息呢?
如果繼續(xù)使用HTTP協(xié)議,可以基于輪詢的方式實(shí)現(xiàn),也就是客戶端每隔一段時(shí)間給服務(wù)器發(fā)請(qǐng)求,看看有沒(méi)有要發(fā)送給我的消息,如果有就獲取到消息,如果沒(méi)有就等待
上述輪詢存在一定問(wèn)題:
- 消耗更多的系統(tǒng)資源,客戶端要頻繁的向服務(wù)器發(fā)請(qǐng)求,而這些請(qǐng)求大多數(shù)是沒(méi)有響應(yīng)的
- 獲取消息不夠及時(shí),只有輪詢的時(shí)候(下次請(qǐng)求的周期)才能夠獲取到消息
如果提高輪詢頻率,則將消耗更多的系統(tǒng)資源,如果降低輪詢頻率,那獲取消息就不夠及時(shí)
此時(shí)可以使用WebSocket協(xié)議,WebSocket協(xié)議也是應(yīng)用層協(xié)議,傳輸層也是基于TCP協(xié)議的,該協(xié)議可以實(shí)現(xiàn)服務(wù)器主動(dòng)向客戶端推送消息的功能
WebSocket的握手階段
先了解一下報(bào)文格式中的幾個(gè)重要信息:
- FIN:表示是否關(guān)閉websocket
- opcode操作碼:描述了當(dāng)前的websocket數(shù)據(jù)幀是起到了啥作用(0x1表示文本數(shù)據(jù),0x2表示二進(jìn)制數(shù)據(jù))
- MASK:是否開(kāi)啟掩碼操作,掩碼操作是為了避免緩沖區(qū)溢出
- payload length:載荷的長(zhǎng)度
- payload data:載荷真正攜帶的數(shù)據(jù)
WebSocket協(xié)議的握手過(guò)程

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

