SpringBoot整合WebSocket實(shí)現(xiàn)聊天室流程全解
什么是WebSocket
WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
WebSocket通信模型
為什么需要WebSocket
http 通信是單向的,發(fā)送請(qǐng)求獲取響應(yīng),沒有請(qǐng)求也就沒有響應(yīng)。
簡單理解:
- HTTP 打電話:客戶端問一句服務(wù)端答一句
- WebSocket 打電話:雙向?qū)υ?/li>
Websocket與http的關(guān)系
相同點(diǎn):
- 都是基于tcp的,都是可靠性傳輸協(xié)議
- 都是應(yīng)用層協(xié)議
不同點(diǎn):
- WebSocket是雙向通信協(xié)議,模擬Socket協(xié)議,可以雙向發(fā)送或接受信息
- HTTP是單向的
- WebSocket是需要瀏覽器和服務(wù)器握手進(jìn)行建立連接的而http是瀏覽器發(fā)起向服務(wù)器的連接,服務(wù)器預(yù)先并不知道這個(gè)連接。
SpringBoot集成WebSocket
maven依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
WebSocketConfig
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
WebSocketService
@Slf4j @Service @ServerEndpoint(value = "/myService/{userId}") public class WebSocketService { /** * 靜態(tài)變量,用來記錄當(dāng)前在線連接數(shù)。應(yīng)該把它設(shè)計(jì)成線程安全的。 */ private static AtomicInteger onlineCount = new AtomicInteger(0);; /** * concurrent包的線程安全Set,用來存放每個(gè)客戶端對(duì)應(yīng)的MyWebSocket對(duì)象。 */ private static ConcurrentHashMap<String,WebSocketService> webSocketMap = new ConcurrentHashMap<>(); /** * concurrent包的線程安全Set,用來存放每個(gè)客戶端對(duì)應(yīng)的session對(duì)象。 */ private static ConcurrentHashMap<String,Session> sessionMap = new ConcurrentHashMap<>(); /** * 與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù) */ private Session session; private String userId = ""; /** * 連接建立成功調(diào)用的方法 */ @OnOpen public void onOpen(Session session,@PathParam("userId") String userId) { this.session = session; this.userId = userId; if(webSocketMap.containsKey(userId) && sessionMap.containsKey(userId)){ webSocketMap.remove(userId); sessionMap.remove(userId); sessionMap.put(userId,session); webSocketMap.put(userId,this); }else{ webSocketMap.put(userId,this); sessionMap.put(userId,session); addOnlineCount(); } log.info("用戶連接:"+userId+",當(dāng)前在線人數(shù)為:" + getOnlineCount()); } /** * 連接關(guān)閉調(diào)用的方法 */ @OnClose public void onClose() { if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); subOnlineCount(); } log.info("用戶退出:"+userId+",當(dāng)前在線人數(shù)為:" + getOnlineCount()); } /** * 收到客戶端消息后調(diào)用的方法 */ @OnMessage public void onMessage(String message, Session session) { this.session = session; log.info("收到客戶端消息 -> {}",message); //服務(wù)端收到客戶端的消息并推送給客戶端 sendMessage(message); } /** * 發(fā)生錯(cuò)誤時(shí)調(diào)用 */ @OnError public void onError(Session session, Throwable error) { log.error(error.getMessage()); } /** * 實(shí)現(xiàn)服務(wù)器主動(dòng)推送 可以通過controller調(diào)用此方法實(shí)現(xiàn)主動(dòng)推送 */ public void sendMessage(String message){ try { Set<Map.Entry<String, Session>> entries = sessionMap.entrySet(); for (Map.Entry<String, Session> next : entries) { Session session = next.getValue(); session.getBasicRemote().sendText(this.userId + "說" + message); } } catch (IOException e) { log.error(e.getMessage()); } } public static synchronized int getOnlineCount() { return onlineCount.get(); } public static synchronized void addOnlineCount() { WebSocketService.onlineCount.getAndIncrement(); } public static synchronized void subOnlineCount() { WebSocketService.onlineCount.getAndDecrement(); } }
WebSocket.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>websocket通訊</title> </head> <body> <p>userId:<input id="userId" name="userId" type="text" value="10"></p> <p>msg:<input id="contentText" name="contentText" type="text" value="hello websocket"></p> <p>操作:<button onclick="openSocket()">開啟socket</button></p> <p>操作:<button onclick="sendMessage()">發(fā)送消息</button></p> </body> <script type="application/javascript"> let socket; function openSocket() { if(socket != null){ socket.close(); socket = null; } let userId = document.getElementById('userId').value socket = new WebSocket("ws://localhost:9000/myService/"+userId); //打開事件 socket.onopen = function() { console.log("websocket已打開"); }; //獲得消息事件 socket.onmessage = function(msg) { console.log(msg.data); }; //關(guān)閉事件 socket.onclose = function() { console.log("websocket已關(guān)閉"); }; //發(fā)生了錯(cuò)誤事件 socket.onerror = function() { console.log("websocket發(fā)生了錯(cuò)誤"); } } function sendMessage() { let contentText = document.getElementById('contentText').value socket.send(contentText); } </script> </html>
測(cè)試
到此這篇關(guān)于SpringBoot整合WebSocket實(shí)現(xiàn)聊天室流程全解的文章就介紹到這了,更多相關(guān)SpringBoot WebSocket聊天室內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring-IOC容器-Bean管理-基于XML方式超詳解
這篇文章主要介紹了Spring為IOC容器Bean的管理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-08-08ThreadPoolExecutor參數(shù)的用法及說明
這篇文章主要介紹了ThreadPoolExecutor參數(shù)的用法及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03java Struts2框架下實(shí)現(xiàn)文件上傳功能
這篇文章主要為大家詳細(xì)介紹了java Struts2框架下實(shí)現(xiàn)文件上傳功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10