WebSocket--簡單介紹

一、什么是 WebSocket?

  • 定義:WebSocket 是一種在單個 TCP 連接上進行全雙工通信的協議。

  • 作用:實現客戶端(瀏覽器)和服務器之間的實時、雙向通信。

  • 優勢

    • 連接保持,通信實時性強(不像 HTTP 需要每次請求都建立連接)。

    • 節省帶寬,減少延遲。

    • 適合聊天室、實時推送、游戲、股票行情、物聯網等場景。


二、WebSocket 工作原理簡述

  1. 客戶端發起 HTTP 請求(帶有 Upgrade: websocket 頭)請求升級協議。

  2. 服務器響應協議升級,建立 WebSocket 連接。

  3. 建立連接后,客戶端和服務器可以隨時互相發送消息,連接保持,直到關閉。

  4. 連接關閉后,雙方不能再通信。


三、Java 原生 WebSocket 使用示例

Java 標準庫中 javax.websocket 提供了 WebSocket 支持。下面示例是一個簡單的服務端:

1. 添加依賴(以 Maven 為例)

<!-- 只在Java EE容器或者支持Java WebSocket API的容器中需要 -->
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version>
</dependency>

2. 編寫WebSocketConfig配置類

Spring Boot 默認不支持直接掃描 @ServerEndpoint,需要做個配置類注冊它:

@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

這個 ServerEndpointExporter 負責注冊所有帶 @ServerEndpoint 注解的 WebSocket 類。

(1)?@ServerEndpoint 注解的類和 Spring 容器
  • 使用標準 Java EE javax.websocket API 的 @ServerEndpoint 注解時,WebSocket 服務端點是由 Java EE 容器(如 Tomcat、Jetty)掃描并管理的,不依賴 Spring 容器。

  • 但是,Spring Boot 本身不會自動掃描和管理@ServerEndpoint 注解的類。


(2) 為什么需要 ServerEndpointExporter
  • ServerEndpointExporter 是 Spring Boot WebSocket 模塊提供的一個 bean,作用是:

    • 讓 Spring Boot 容器掃描并注冊所有用 @ServerEndpoint 注解的類。

    • 把這些端點交給底層的 WebSocket 容器(Tomcat 等)管理。


(3) 什么時候需要 ServerEndpointExporter
  • 如果你用的是 Spring Boot 嵌入式 Tomcat 或其他容器,且你的 WebSocket 端點是用標準的 @ServerEndpoint 注解實現的,通常需要配置這個 bean

3. 編寫 WebSocket 服務器端

@ServerEndpoint("/myWs")
@Component
@Slf4j
public class WsServerEndpont {static Map<String,Session> sessionMap = new ConcurrentHashMap<>();//連接建立時執行的操作@OnOpenpublic void onOpen(Session session){sessionMap.put(session.getId(),session);log.info("websocket is open");}//收到了客戶端消息執行的操作@OnMessagepublic String onMessage(String text){log.info("收到了一條消息:"+text);return "已收到你的消息";}//連接關閉的時候執行的操作@OnClosepublic void onClose(Session session){sessionMap.remove(session.getId());log.info("websocket is close");}//每2s發送給客戶端心跳消息@Scheduled(fixedRate = 2000)public void sendMsg() throws IOException {for(String key:sessionMap.keySet()){sessionMap.get(key).getBasicRemote().sendText("心跳");}}
}

3. 部署運行

  • 需要在支持 Java EE WebSocket 的容器中(如 Tomcat 8+、Jetty、Glassfish)部署。

  • 客戶端可用瀏覽器或工具連接:ws://localhost:8080/your-app/websocket


四、Spring Boot 中使用 WebSocket(實現多人聊天)

????????Spring Boot 提供了非常方便的 WebSocket 支持,通常結合 STOMP 協議和 SockJS 來實現消息的訂閱和廣播。

1. 添加依賴(Maven)

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

Spring Boot 的依賴管理機制(依賴版本繼承)

  • Spring Boot 使用了 “依賴管理(Dependency Management)”,這是它的核心特性之一。

  • 你只需在你的項目中聲明 spring-boot-starter-parent 或通過 spring-boot-dependencies 管理依賴版本,Spring Boot 會自動幫你管理和統一版本號。

2. 配置 MyWsConfig

// WebSocket配置類,注冊WebSocket處理器和攔截器
@Configuration
@EnableWebSocket  // 啟用WebSocket支持
public class MyWsConfig implements WebSocketConfigurer {@ResourceMyWsHandler myWsHandler;  // 自定義的WebSocket處理器@ResourceMyWsInterceptor myWsInterceptor; // 自定義的握手攔截器@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 注冊WebSocket處理器,路徑為/myWs1// 添加握手攔截器,允許所有源跨域請求(*)registry.addHandler(myWsHandler, "/myWs1")        // 注冊WebSocket處理器及路徑.addInterceptors(myWsInterceptor)          // 添加握手階段的攔截器,做一些額外處理.setAllowedOrigins("*");                    // 允許所有域發起跨域連接請求}
}
2.1 代碼講解:
(1)?addHandler(myWsHandler, "/myWs1")
  • 作用:給 WebSocket 服務器注冊一個處理器(Handler),并指定客戶端連接時使用的 URL 路徑。

  • myWsHandler 是你自定義的 WebSocket 業務處理類(繼承自 WebSocketHandlerAbstractWebSocketHandler)。

  • "/myWs1" 是 WebSocket 連接的訪問路徑,客戶端通過 ws://服務器地址/myWs1 來建立 WebSocket 連接。

總結:這句相當于告訴服務器,“當客戶端請求 /myWs1 路徑時,交給 myWsHandler 來處理這次 WebSocket 連接和消息。”


(2)addInterceptors(myWsInterceptor)
  • 作用:給這個 WebSocket 處理器綁定一個或多個攔截器(HandshakeInterceptor),用來攔截 WebSocket 握手階段的請求。

  • 握手階段是 WebSocket 建立連接的第一步,類似 HTTP 請求升級。

  • 你可以在攔截器里做一些額外操作,比如:

    • 記錄日志

    • 權限校驗

    • 傳遞用戶信息到 WebSocket Session

    • 修改握手請求和響應

  • 你的 myWsInterceptor 繼承自 HttpSessionHandshakeInterceptor,默認支持把 HTTP Session 關聯到 WebSocket Session。

總結:這句是告訴服務器,“在建立 WebSocket 連接握手時,執行 myWsInterceptor 中的邏輯。”


(3)setAllowedOrigins("*")
  • 作用:設置允許連接的客戶端來源(Origin),即支持跨域連接的源。

  • 這里 "*" 表示允許所有源都能連接你的 WebSocket 服務。

  • Origin 是瀏覽器在 WebSocket 握手請求頭里自動帶上的,服務器根據它判斷是否允許該請求。

  • 注意:生產環境一般不要用 "*",建議指定可信的域名,如 setAllowedOrigins("https://example.com")

總結:這句是告訴服務器,“允許哪些來源的網頁可以發起 WebSocket 連接請求”。

3. 配置 MyWsHandler

@Slf4j
@Component
public class MyWsHandler extends AbstractWebSocketHandler {// 存放所有連接的客戶端會話,線程安全的Mapprivate static Map<String, SessionBean> sessionBeanMap;// 用于生成客戶端唯一ID的原子計數器private static AtomicInteger clientIdMaker;// 用于存放群聊消息的緩沖區private static StringBuffer stringBuffer;static {sessionBeanMap = new ConcurrentHashMap<>();clientIdMaker = new AtomicInteger(0);stringBuffer = new StringBuffer();}/*** 連接建立成功時調用//相當于上面原生的onOpe******************************/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {super.afterConnectionEstablished(session);// 新建SessionBean,給該連接分配唯一clientIdSessionBean sessionBean = new SessionBean(session, clientIdMaker.getAndIncrement());// 放入sessionMap管理sessionBeanMap.put(session.getId(), sessionBean);log.info(sessionBean.getClientId() + "建立了連接");// 群聊消息中記錄進入群聊通知stringBuffer.append(sessionBean.getClientId() + "進入了群聊<br/>");// 給所有客戶端發送當前群聊消息sendMessage(sessionBeanMap);}/*** 收到客戶端消息時調用//相當于上面原生的onMessage*********************************/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {super.handleTextMessage(session, message);// 記錄日志,打印客戶端發送的消息log.info(sessionBeanMap.get(session.getId()).getClientId() + ":" + message.getPayload());// 將消息追加到群聊緩沖區stringBuffer.append(sessionBeanMap.get(session.getId()).getClientId() + ":" + message.getPayload() + "<br/>");// 廣播給所有客戶端sendMessage(sessionBeanMap);}/*** 傳輸發生異常時調用*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {super.handleTransportError(session, exception);// 如果會話還開著,先關閉if (session.isOpen()) {session.close();}// 移除該會話sessionBeanMap.remove(session.getId());}/*** 連接關閉時調用 //相當于上面原生的onClose*****************************/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {super.afterConnectionClosed(session, status);// 獲取即將關閉連接的clientIdint clientId = sessionBeanMap.get(session.getId()).getClientId();// 移除關閉的會話sessionBeanMap.remove(session.getId());log.info(clientId + "關閉了連接");// 群聊消息中記錄退出通知stringBuffer.append(clientId + "退出了群聊<br/>");// 廣播給所有客戶端sendMessage(sessionBeanMap);}/*** 給所有客戶端發送消息*/public void sendMessage(Map<String, SessionBean> sessionBeanMap) {for (String key : sessionBeanMap.keySet()) {try {// 發送群聊緩沖區里的消息給每個客戶端sessionBeanMap.get(key).getWebSocketSession().sendMessage(new TextMessage(stringBuffer.toString()));} catch (IOException e) {// 日志記錄異常log.error(e.getMessage());}}}
}

4. 配置 MyWsInterceptor

@Slf4j
@Component
public class MyWsInterceptor extends HttpSessionHandshakeInterceptor {/*** 握手之前調用,可用于記錄日志或做權限校驗*/@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {log.info(request.getRemoteAddress().toString() + "開始握手");return super.beforeHandshake(request, response, wsHandler, attributes);}/*** 握手完成調用*/@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,WebSocketHandler wsHandler, Exception ex) {log.info(request.getRemoteAddress().toString() + "完成握手");super.afterHandshake(request, response, wsHandler, ex);}
}

5. 配置 SessionBean


// 簡單的會話封裝類,保存WebSocketSession和客戶端唯一ID
@AllArgsConstructor
@Data
public class SessionBean {private WebSocketSession webSocketSession;private Integer clientId;
}

6. 啟動類WsDemoApplication?

// 啟動類,開啟定時任務支持
@EnableScheduling
@SpringBootApplication
public class WsDemoApplication {public static void main(String[] args) {SpringApplication.run(WsDemoApplication.class, args);}
}

7. 前端示例(HTML + JavaScript)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>ws client</title>
</head>
<body>
<p style="border:1px solid black;width: 600px;height: 500px" id="talkMsg"></p>
<input id="message" /><button id="sendBtn" onclick="sendMsg()">發送</button>
</body>
<script>let ws = new WebSocket("ws://localhost:8080/myWs1")// ws.onopen=function () {// }ws.onmessage=function (message) {document.getElementById("talkMsg").innerHTML = message.data}function sendMsg() {ws.send(document.getElementById("message").value)document.getElementById("message").value=""}
</script>
</html>

五、小結

特點Java 原生 WebSocketSpring Boot WebSocket + STOMP
適用場景簡單直接的 WebSocket 應用需要消息訂閱/廣播、復雜消息路由的應用
配置復雜度低(容器支持即可)需要配置消息代理,依賴更多
功能支持基礎雙向通信支持訂閱、廣播、分組、消息轉換
前端開發支持需自行實現協議使用 STOMP.js 等庫簡化開發

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918383.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918383.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918383.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【STM32 LWIP配置】STM32H723ZG + Ethernet +LWIP 配置 cubemx

STM32H723ZG LAN8742 Ethernet LWIP 配置 cubemx &#x1f31e;這邊記錄一下這塊mcu 配置以太網的過程&#xff0c;IDE是KEIL MDK&#xff0c;其實就是在下面多次提到的blog的基礎上 在scatter file進行配置 首先&#xff0c;如果想要簡單一點 直接去cubemx 那邊獲取相關的例…

EI檢索-學術會議 | 人工智能、虛擬現實、可視化

第五屆人工智能、虛擬現實與可視化國際學術會議&#xff08;AIVRV 2025&#xff09;定于2025年9月5-7日在中國 成都召開。人工智能正驅動各行業智能化轉型&#xff0c;提升效率與質量&#xff1b;虛擬現實技術以其沉浸感重塑教育、娛樂、醫療等領域體驗&#xff1b;可視化技術…

力扣(H指數)

一、題目分析 &#xff08;一&#xff09;問題描述 給定一個整數數組 citations&#xff0c;其中 citations[i] 表示研究者的第 i 篇論文被引用的次數。我們需要計算并返回該研究者的 H 指數。根據維基百科定義&#xff1a;H 指數代表“高引用次數”&#xff0c;一名科研人員的…

標準io(1)

標準I/O基礎概念標準I/O&#xff08;Standard Input/Output&#xff09;是C語言提供的一組高級文件操作函數&#xff0c;位于<stdio.h>頭文件中。與低級I/O&#xff08;如Unix的系統調用read/write&#xff09;相比&#xff0c;標準I/O引入了緩沖機制&#xff0c;能顯著提…

線性代數1000題學習筆記

1000題線代基礎第一章1-101000題線代基礎第二章1-171000題線代基礎第三章1-11

LeetCode算法日記 - Day 8: 串聯所有單詞的子串、最小覆蓋子串

目錄 1.串聯所有單詞的子串 1.2 解法 1.3 代碼實現 2. 最小覆蓋子串 2.1 題目解析 2.2 解法 2.3 代碼實現 1.串聯所有單詞的子串 30. 串聯所有單詞的子串 - 力扣&#xff08;LeetCode&#xff09; 給定一個字符串 s 和一個字符串數組 words。 words 中所有字符串 長度…

linux實戰:基于Ubuntu的專業相機

核心組件就是QTimerOpenCV的組合方案攝像頭啟停控制用QPushButton實現&#xff0c;幀顯示必須用QLabel而不能用普通控件&#xff0c;視頻流刷新用QTimer比多線程更簡單想快速實現攝像頭控制功能&#xff0c;核心組件就是QTimerOpenCV的組合方案。攝像頭啟停控制用QPushButton實…

《深度剖析前端框架中錯誤邊界:異常處理的基石與進階》

錯誤邊界作為一種特殊的組件機制&#xff0c;正悄然重塑著應用應對異常的底層邏輯。它并非簡單的代碼片段組合&#xff0c;而是一套貫穿組件生命周期的防護體系&#xff0c;其核心價值在于將局部錯誤的影響牢牢鎖定在可控范圍內&#xff0c;避免整個應用陷入不可挽回的崩潰狀態…

6GB顯存玩轉SD微調!LoRA-scripts本地部署教程,一鍵煉出專屬AI畫師

一、介紹LoRA-scripts&#xff08;又名 SD-Trainer&#xff09;&#xff0c;是一個專為訓練低秩自適應&#xff08;LoRA&#xff09;模型設計的開源工具集主要應用于Stable Diffusion等AI繪圖模型的微調&#xff0c;幫助用戶高效創建定制化風格、角色或概念的輕量級模型。目前已…

探索AI的數學奇跡:Gemini 2.5 Pro如何摘得IMO金牌

?? 引言:從人類天才到AI奇才的跨越 想象一下,一個AI模型坐在國際數學奧林匹克(IMO)的考場里,手里拿著筆(好吧,其實是處理token),面對那些讓高中生們頭疼不已的難題。它不是靠死記硬背,而是通過深思熟慮的推理,一步步攻克難關。這聽起來像科幻小說,但2025年,這已…

MCP學習與實踐

目錄 1.MCP簡介 1.1 MCP是什么 1.2 MCP與Agent關系&#xff1a; 1.3 MCP的架構 2. MCP原理 2.1 MCP 工作過程 2.2 MCP 通訊方式 2. MCP使用 2.1 cursor中增加MCP-SSE(高德地圖MCP) 2.2 cursor中增加MCP-STDIO&#xff08;12306-MCP&#xff09; 本文詳細講解了什么是…

MySQL(187)如何使用pt-query-digest進行查詢分析?

使用 pt-query-digest 工具可以幫助分析 MySQL 查詢的性能&#xff0c;找出慢查詢、頻繁查詢以及消耗資源較多的查詢&#xff0c;從而為優化提供依據。以下是詳細深入的使用 pt-query-digest 進行查詢分析的步驟和相關示例。 一、安裝 pt-query-digest pt-query-digest 是 Perc…

分享一個基于Python和Hadoop的的電信客戶特征可視化分析平臺 基于Spark平臺的電信客服數據存儲與處理系統源碼

&#x1f495;&#x1f495;作者&#xff1a;計算機源碼社 &#x1f495;&#x1f495;個人簡介&#xff1a;本人八年開發經驗&#xff0c;擅長Java、Python、PHP、.NET、Node.js、Spark、hadoop、Android、微信小程序、爬蟲、大數據、機器學習等&#xff0c;大家有這一塊的問題…

初識STL

一 、STL的誕生在C發展早期&#xff0c;程序員在不同的項目中需要反復編寫相似的數據結構和算法。重復開發帶來以下問題&#xff1a;代碼冗余&#xff1a;每個項目都要重新實現基本數據結構和算法維護困難&#xff1a;不同人編寫的代碼風格不一致&#xff0c;難以維護效率低下&…

DDoS 防護的未來趨勢:AI 如何重塑安全行業?

隨著網絡攻擊規模和復雜性的不斷升級&#xff0c;分布式拒絕服務&#xff08;DDoS&#xff09;攻擊已成為企業數字化轉型中的一大威脅。傳統防御手段在應對智能化、動態化的攻擊時逐漸顯露出局限性。而人工智能&#xff08;AI&#xff09;技術的崛起&#xff0c;正為 DDoS 防護…

【每天一個知識點】深度領域對抗神經網絡

Deep Domain Adversarial Neural Network&#xff08;深度領域對抗神經網絡&#xff0c;DDANN&#xff09; 是一類結合 深度學習 與 領域自適應&#xff08;domain adaptation&#xff09; 思想的神經網絡結構&#xff0c;主要用于不同數據域之間的知識遷移&#xff0c;尤其是在…

【C語言】深入理解預處理

文章目錄一、預定義符號二、#define定義常量&#xff1a;便捷的符號替換常見用法示例&#xff1a;注意事項&#xff1a;三、#define定義宏&#xff1a;帶參數的文本替換關鍵注意點&#xff1a;四、帶有副作用的宏參數五、宏替換的規則&#xff1a;預處理的執行步驟重要注意&…

展銳平臺(Android15)WLAN熱點名稱修改不生效問題分析

前言 在展銳Android V項目開發中&#xff0c;需要修改softAp/P2P熱點名稱時&#xff0c;發現集成GMS后直接修改framework層代碼無效。具體表現為&#xff1a; 修改packages/modules/Wifi/WifiApConfigStore中的getDefaultApConfiguration方法編譯燒錄后修改不生效 問題根源在…

wsl ubuntu訪問(掛載)vmware vmdk磁盤教程

之前使用VMware Workstation 虛擬機跑了個ubuntu&#xff0c;現在改用wsl了&#xff0c; 想把vmware的磁盤掛載到wsl ubuntu。一、磁盤合并我原先的vmware跑的ubuntu存在多個vmdk文件&#xff08;磁盤文件&#xff09;&#xff0c;需要先將磁盤合并成一個才方便掛載。首先你電腦…

UGUI源碼剖析(3):布局的“原子”——RectTransform的核心數據模型與幾何學

UGUI源碼剖析&#xff08;第三章&#xff09;&#xff1a;布局的“原子”——RectTransform的核心數據模型與幾何學 在前幾章中&#xff0c;我們了解了UGUI的組件規范和更新調度機制。現在&#xff0c;我們將深入到這個系統的“幾何學”核心&#xff0c;去剖析那個我們每天都在…