通過(guò)實(shí)例講解springboot整合WebSocket
一、背景
我們都知道 http 協(xié)議只能瀏覽器單方面向服務(wù)器發(fā)起請(qǐng)求獲得響應(yīng),服務(wù)器不能主動(dòng)向?yàn)g覽器推送消息。想要實(shí)現(xiàn)瀏覽器的主動(dòng)推送有兩種主流實(shí)現(xiàn)方式:
- 輪詢:缺點(diǎn)很多,但是實(shí)現(xiàn)簡(jiǎn)單
- websocket:在瀏覽器和服務(wù)器之間建立 tcp 連接,實(shí)現(xiàn)全雙工通信
springboot 使用 websocket 有兩種方式,一種是實(shí)現(xiàn)簡(jiǎn)單的 websocket,另外一種是實(shí)現(xiàn)STOMP協(xié)議。這一篇實(shí)現(xiàn)簡(jiǎn)單的 websocket,STOMP 下一篇在講。
注意:如下都是針對(duì)使用 springboot 內(nèi)置容器
二、實(shí)現(xiàn)
1、依賴引入
要使用 websocket 關(guān)鍵是@ServerEndpoint這個(gè)注解,該注解是 javaee 標(biāo)準(zhǔn)中的注解,tomcat7 及以上已經(jīng)實(shí)現(xiàn)了,如果使用傳統(tǒng)方法將 war 包部署到 tomcat 中,只需要引入如下 javaee 標(biāo)準(zhǔn)依賴即可:
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>
如使用 springboot 內(nèi)置容器,無(wú)需引入,springboot 已經(jīng)做了包含。我們只需引入如下依賴即可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>1.5.3.RELEASE</version> <type>pom</type> </dependency>
2、注入 Bean
首先注入一個(gè)ServerEndpointExporterBean,該 Bean 會(huì)自動(dòng)注冊(cè)使用@ServerEndpoint 注解申明的 websocket endpoint。代碼如下:
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
3、申明 endpoint
建立MyWebSocket.java類,在該類中處理 websocket 邏輯
@ServerEndpoint(value = "/websocket") //接受websocket請(qǐng)求路徑 @Component //注冊(cè)到spring容器中 public class MyWebSocket { //保存所有在線socket連接 private static Map<String,MyWebSocket> webSocketMap = new LinkedHashMap<>(); //記錄當(dāng)前在線數(shù)目 private static int count=0; //當(dāng)前連接(每個(gè)websocket連入都會(huì)創(chuàng)建一個(gè)MyWebSocket實(shí)例 private Session session; private Logger log = LoggerFactory.getLogger(this.getClass()); //處理連接建立 @OnOpen public void onOpen(Session session){ this.session=session; webSocketMap.put(session.getId(),this); addCount(); log.info("新的連接加入:{}",session.getId()); } //接受消息 @OnMessage public void onMessage(String message,Session session){ log.info("收到客戶端{(lán)}消息:{}",session.getId(),message); try{ this.sendMessage("收到消息:"+message); }catch (Exception e){ e.printStackTrace(); } } //處理錯(cuò)誤 @OnError public void onError(Throwable error,Session session){ log.info("發(fā)生錯(cuò)誤{},{}",session.getId(),error.getMessage()); } //處理連接關(guān)閉 @OnClose public void onClose(){ webSocketMap.remove(this.session.getId()); reduceCount(); log.info("連接關(guān)閉:{}",this.session.getId()); } //群發(fā)消息 //發(fā)送消息 public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } //廣播消息 public static void broadcast(){ MyWebSocket.webSocketMap.forEach((k,v)->{ try{ v.sendMessage("這是一條測(cè)試廣播"); }catch (Exception e){ } }); } //獲取在線連接數(shù)目 public static int getCount(){ return count; } //操作count,使用synchronized確保線程安全 public static synchronized void addCount(){ MyWebSocket.count++; } public static synchronized void reduceCount(){ MyWebSocket.count--; } }
4、客戶的實(shí)現(xiàn)
客戶端使用 h5 原生 websocket,部分瀏覽器可能不支持。代碼如下:
<html> <head> <title>websocket測(cè)試</title> <meta charset="utf-8" /> </head> <body> <button onclick="sendMessage()">測(cè)試</button> <script> let socket = new WebSocket("ws://localhost:8080/websocket"); socket.onerror = err => { console.log(err); }; socket.onopen = event => { console.log(event); }; socket.onmessage = mess => { console.log(mess); }; socket.onclose = () => { console.log("連接關(guān)閉"); }; function sendMessage() { if (socket.readyState === 1) socket.send("這是一個(gè)測(cè)試數(shù)據(jù)"); else alert("尚未建立websocket連接"); } </script> </body> </html>
三、測(cè)試
建立一個(gè) controller 測(cè)試群發(fā),代碼如下:
@RestController public class HomeController { @GetMapping("/broadcast") public void broadcast(){ MyWebSocket.broadcast(); } }
然后打開(kāi)上面的 html,可以看到瀏覽器和服務(wù)器都輸出連接成功的信息:
瀏覽器: Event {isTrusted: true, type: "open", target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …} 服務(wù)端: 2018-08-01 14:05:34.727 INFO 12708 --- [nio-8080-exec-1] com.fxb.h5websocket.MyWebSocket : 新的連接加入:0
點(diǎn)擊測(cè)試按鈕,可在服務(wù)端看到如下輸出:
2018-08-01 15:00:34.644 INFO 12708 --- [nio-8080-exec-6] com.fxb.h5websocket.MyWebSocket : 收到客戶端2消息:這是一個(gè)測(cè)試數(shù)據(jù)
再次打開(kāi) html 頁(yè)面,這樣就有兩個(gè) websocket 客戶端,然后在瀏覽器訪問(wèn)localhost:8080/broadcast測(cè)試群發(fā)功能,每個(gè)客戶端都會(huì)輸出如下信息:
MessageEvent {isTrusted: true, data: "這是一條測(cè)試廣播", origin: "ws://localhost:8080", lastEventId: "", source: null, …}
源碼可在 github 上下載
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- SpringBoot3集成WebSocket的全過(guò)程
- SpringBoot整合Netty+Websocket實(shí)現(xiàn)消息推送的示例代碼
- springboot整合websocket后啟動(dòng)報(bào)錯(cuò)(javax.websocket.server.ServerContainer not available)
- SpringBoot 整合WebSocket 前端 uniapp 訪問(wèn)的詳細(xì)方法
- Springboot整合WebSocket實(shí)戰(zhàn)教程
- SpringBoot整合WebSocket的客戶端和服務(wù)端的實(shí)現(xiàn)代碼
- SpringBoot整合Netty實(shí)現(xiàn)WebSocket的示例代碼
- springboot整合websocket最基礎(chǔ)入門使用教程詳解
- SpringBoot3整合WebSocket詳細(xì)指南
相關(guān)文章
一文詳解如何通過(guò)Java實(shí)現(xiàn)SSL交互功能
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Java實(shí)現(xiàn)SSL交互功能,文中的示例代碼講解詳細(xì),對(duì)我們的學(xué)習(xí)或工作有一定的幫助,需要的可以參考一下2023-04-04SpringCloud可視化鏈路追蹤系統(tǒng)Zipkin部署過(guò)程
這篇文章主要介紹了SpringCloud可視化鏈路追蹤系統(tǒng)Zipkin部署過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Feign調(diào)用服務(wù)時(shí)丟失Cookie和Header信息的解決方案
這篇文章主要介紹了Feign調(diào)用服務(wù)時(shí)丟失Cookie和Header信息的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03SpringBoot整合JavaMail實(shí)現(xiàn)發(fā)郵件的項(xiàng)目實(shí)踐
本文主要介紹了SpringBoot整合JavaMail實(shí)現(xiàn)發(fā)郵件的項(xiàng)目實(shí)踐,詳細(xì)闡述了使用SpringBoot和JavaMail發(fā)送郵件的步驟,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10淺談Java中Unicode的編碼和實(shí)現(xiàn)
這篇文章向大家介紹了Java編程中Unicode編碼及實(shí)現(xiàn)的相關(guān)內(nèi)容,列舉了幾個(gè)字符不同表達(dá)式的比較,以及Unicode平面映射的知識(shí),具有一點(diǎn)點(diǎn)參考價(jià)值,需要的朋友可以了解下。2017-10-10解決fastjson從1.1.41升級(jí)到1.2.28后報(bào)錯(cuò)問(wèn)題詳解
這篇文章主要介紹了解決fastjson從1.1.41升級(jí)到1.2.28后報(bào)錯(cuò)問(wèn)題詳解,需要的朋友可以參考下2020-02-02Java實(shí)現(xiàn)XML格式與JSON格式互相轉(zhuǎn)換的方法
這篇文章主要介紹了Java實(shí)現(xiàn)XML格式與JSON格式互相轉(zhuǎn)換的方法,方法通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),選擇使用哪種格式通常取決于項(xiàng)目的需求和上下文,所以格式轉(zhuǎn)換就成了我們必備的技能,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧2023-10-10java獲取文件的inode標(biāo)識(shí)符的方法
這篇文章主要介紹了java獲取文件的inode標(biāo)識(shí)符,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04java實(shí)現(xiàn)在一張大圖片上添加小圖及文字
這篇文章主要介紹了java實(shí)現(xiàn)在一張大圖上添加小圖及文字,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11