您好,登錄后才能下訂單哦!
之前實現WebSocket基于STOMP的,覺得SpringBoot封裝的太高,不怎么靈活,現在實現一個純H5的,也大概了解webSocket在內部是怎么傳輸的。
1.環境搭建
因為在上一篇基于STOMP協議實現的WebSocket里已經有大概介紹過Web的基本情況了,所以在這篇就不多說了,我們直接進入正題吧,在SpringBoot中,我們還是需要導入WebSocket的包。
在pox.xml加上對springBoot對WebSocket的支持:
<!-- webSocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
這里大概說一下自己的一點小見解:客戶端與服務器建立WebSocket連接,實際上是創建了一個Socket,這個Socket是共享與客戶端和服務器的。兩者只要往對應的Socket里操作,就可以實現雙方實時通訊了
2.編碼實現
一、在SpringBoot中,添加WebSocket的配置
package com.cloud.sbjm.configure; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import com.cloud.sbjm.security.WebSocketInterceptor; import com.cloud.sbjm.service.Imp.MyHandler; //實現接口來配置Websocket請求的路徑和攔截器。 @Configuration @EnableWebSocket public class WebSocketH5Config implements WebSocketConfigurer{ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { //handler是webSocket的核心,配置入口 registry.addHandler(new MyHandler(), "/myHandler/{ID}").setAllowedOrigins("*").addInterceptors(new WebSocketInterceptor()); } }
1.@Configuration:注解標識該類為Spring的配置類
2.@EnableWebSocket:開啟注解接收和發送消息
3.實現WebSocketConfigurer接口,重寫registerWebSocketHandlers方法,這是一個核心實現方法,配置websocket入口,允許訪問的域、注冊Handler、定義攔截器。客戶端通過“/myHandler/{ID}”直接訪問Handler核心類,進行socket的連接、接收、發送等操作,這里由于還加了個攔截器,所以建立新的socket訪問時,都先進來攔截器再進去Handler類,“new WebSocketInterceptor()”是我實現的攔截器,“new MyHandler()”是我實現的一個Handler類。
二、WebSocketInterceptor攔截器的實現:
package com.cloud.sbjm.security; import java.util.Map; import javax.servlet.http.HttpSession; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; public class WebSocketInterceptor implements HandshakeInterceptor { //進入hander之前的攔截 @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception { if (request instanceof ServletServerHttpRequest) { String ID = request.getURI().toString().split("ID=")[1]; System.out.println("當前session的ID="+ID); ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request; HttpSession session = serverHttpRequest.getServletRequest().getSession(); map.put("WEBSOCKET_USERID",ID); } return true; } @Override public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) { System.out.println("進來webSocket的afterHandshake攔截器!"); } }
1.實現了HandshakeInterceptor 接口,并實現了beforeHandshake該方法,該方法是在進入Handler核心類之前進行攔截。
這里主要實現的邏輯是:
截取客戶端建立webSocket連接時發送的URL地址字符串,并通過對該字符串進行特殊標識截取操作,獲取客戶端發送的唯一標識(由自己定義的,一般是系統用戶ID唯一標識,用以標識該用戶),并把它以鍵值對的形式放到Session里,這樣后期可以通過該session獲取它對應的用戶ID了。【一個session對應著一個webSocketSession】
三、MyHandler核心類的實現
package com.cloud.sbjm.service.Imp; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; import net.sf.json.JSONObject; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; @Service public class MyHandler implements WebSocketHandler { //在線用戶列表 private static final Map<String, WebSocketSession> users; static { users = new HashMap<>(); } //新增socket @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("成功建立連接"); String ID = session.getUri().toString().split("ID=")[1]; System.out.println(ID); if (ID != null) { users.put(ID, session); session.sendMessage(new TextMessage("成功建立socket連接")); System.out.println(ID); System.out.println(session); } System.out.println("當前在線人數:"+users.size()); } //接收socket信息 @Override public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception { try{ JSONObject jsonobject = JSONObject.fromObject(webSocketMessage.getPayload()); System.out.println(jsonobject.get("id")); System.out.println(jsonobject.get("message")+":來自"+(String)webSocketSession.getAttributes().get("WEBSOCKET_USERID")+"的消息"); sendMessageToUser(jsonobject.get("id")+"",new TextMessage("服務器收到了,hello!")); }catch(Exception e){ e.printStackTrace(); } } /** * 發送信息給指定用戶 * @param clientId * @param message * @return */ public boolean sendMessageToUser(String clientId, TextMessage message) { if (users.get(clientId) == null) return false; WebSocketSession session = users.get(clientId); System.out.println("sendMessage:" + session); if (!session.isOpen()) return false; try { session.sendMessage(message); } catch (IOException e) { e.printStackTrace(); return false; } return true; } /** * 廣播信息 * @param message * @return */ public boolean sendMessageToAllUsers(TextMessage message) { boolean allSendSuccess = true; Set<String> clientIds = users.keySet(); WebSocketSession session = null; for (String clientId : clientIds) { try { session = users.get(clientId); if (session.isOpen()) { session.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); allSendSuccess = false; } } return allSendSuccess; } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { if (session.isOpen()) { session.close(); } System.out.println("連接出錯"); users.remove(getClientId(session)); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { System.out.println("連接已關閉:" + status); users.remove(getClientId(session)); } @Override public boolean supportsPartialMessages() { return false; } /** * 獲取用戶標識 * @param session * @return */ private Integer getClientId(WebSocketSession session) { try { Integer clientId = (Integer) session.getAttributes().get("WEBSOCKET_USERID"); return clientId; } catch (Exception e) { return null; } } }
1.實現了WebSocketHandler接口,并實現了關鍵的幾個方法。
① afterConnectionEstablished(接口提供的):建立新的socket連接后回調的方法。主要邏輯是:將成功建立連接的webSocketSssion放到定義好的常量[private static final Map<String, WebSocketSession> users;]中去。這里也截取客戶端訪問的URL的字符串,拿到標識,以鍵值對的形式講每一個webSocketSession存到users里,以記錄每個Socket。
② handleMessage(接口提供的):接收客戶端發送的Socket。主要邏輯是:獲取客戶端發送的信息。這里之所以可以獲取本次Socket的ID,是因為客戶端在第一次進行連接時,攔截器進行攔截后,設置好ID,這樣也說明,雙方在相互通訊的時候,只是對第一次建立好的socket持續進行操作。
③ sendMessageToUser(自己定義的):發送給指定用戶信息。主要邏輯是:根據用戶ID從常量users(記錄每一個Socket)中,獲取Socket,往該Socket里發送消息,只要客戶端還在線,就能收到該消息。
④sendMessageToAllUsers (自己定義的):這個廣播消息,發送信息給所有socket。主要邏輯是:跟③類型,只不過是遍歷整個users獲取每一個socket,給每一個socket發送消息即可完廣播發送
⑤handleTransportError(接口提供的):連接出錯時,回調的方法。主要邏輯是:一旦有連接出錯的Socket,就從users里進行移除,有提供該Socket的參數,可直接獲取ID,進行移除。這個在客戶端沒有正常關閉連接時,會進來,所以在開發客戶端時,記得關閉連接
⑥afterConnectionClosed(接口提供的):連接關閉時,回調的方法。主要邏輯:一旦客戶端/服務器主動關閉連接時,將個socket從users里移除,有提供該Socket的參數,可直接獲取ID,進行移除。
后臺的開發就開發完了,大家有沒有發現比基于STOMP協議實現要靈活得多?
四、客戶端頁面的實現【基于H5】
不需要加入任何的JS包
<!DOCTYPE html> <html> <head> <title>socket.html</title> <meta name="keywords" content="keyword1,keyword2,keyword3"> <meta name="description" content="this is my page"> <meta name="content-type" content="text/html" charset="UTF-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css" rel="external nofollow" >--> </head> <body> Welcome<br/> <input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button> <div id="message"> </div> <!-- 公共JS --> <script type="text/javascript" src="../webSocket/jquery.min.js"></script> <script type="text/javascript"> var userID="888"; var websocket=null; $(function() { //創建WebSocket connectWebSocket(); }) //強制關閉瀏覽器 調用websocket.close(),進行正常關閉 window.onunload = function() { //關閉連接 closeWebSocket(); } //建立WebSocket連接 function connectWebSocket(){ console.log("開始..."); //建立webSocket連接 websocket = new WebSocket("ws://127.0.0.1:9091/cloud-sbjm/myHandler/ID="+userID); //打開webSokcet連接時,回調該函數 websocket.onopen = function () { console.log("onpen"); } //關閉webSocket連接時,回調該函數 websocket.onclose = function () { //關閉連接 console.log("onclose"); } //接收信息 websocket.onmessage = function (msg) { console.log(msg.data); } } //發送消息 function send(){ var postValue={}; postValue.id=userID; postValue.message=$("#text").val(); websocket.send(JSON.stringify(postValue)); } //關閉連接 function closeWebSocket(){ if(websocket != null) { websocket.close(); } } </script> </body> </html>
頁面比較簡單,簡單解釋一下:
1.new WebSocket("ws://127.0.0.1:9091/cloud-sbjm/myHandler/ID="+userID),與服務器建立webSocket連接,后面的ID="+userID,是動態參數,跟服務器配置Handler的訪問地址時對應"/myHandler/{ID}"。
2.H5也提供多個回調函數
onopen:打開webSokcet連接時,回調該函數
onclose:關閉webSocket連接時,回調該函數
onmessage:服務器給該socket發送消息時,回調該函數,獲取消息
websocket.send(JSON.stringify(postValue));:給Socket發送消息,服務器獲取
websocket.close();客戶端主要關閉連接,會觸發客戶端的onclose方法和服務器的afterConnectionClosed方法
到此服務端的開發也完成了,下面執行一下程序效果圖:
一、建立連接
客戶端:
服務器:
二、發送消息
客戶端:
服務器:
三、服務器主動推送消息
服務器代碼:
到此已經完成了,各位可以根據自己需求進行修改,這會靈活多了!
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。