http協議在通訊過程當中存在一個巨大的缺陷,通訊只能由客戶端發起,服務器只能根據響應返回響應的結果。也就說,服務器端沒法主動給客戶端發送消息。html
對於服務器端連續的狀態變化,http協議就顯得有些力不從心了,固然也能夠經過其餘的方式實現。好比:java
雖然這樣也能夠實現咱們的要求,可是資源就在輪詢的過程當中被大量浪費。web
WebSocket協議,2008年誕生,2011年稱爲國際標準,其最大的特色就是服務器端能夠主動向客戶端發送消息,實現真正的雙向平等對話。主要特色:redis
理論沒看懂的能夠戳這 故事描述型spring
建立WebSocketapi
var Socket = new WebSocket(url, [protocol] );//協議能夠爲空
屬性緩存
Socket.readyState//鏈接狀態 Socket.bufferedAmount //隊列中等待傳輸,可是尚未發出的 UTF-8 文本字節數。
事件,編寫的時候要加上on 好比onOpen安全
open //鏈接創建時觸發 message //客戶端接收服務端數據時觸發 error //通訊發生錯誤時觸發 close //鏈接關閉時觸發
方法服務器
Socket.send() //使用鏈接發送數據 Socket.close() //關閉鏈接
實例:websocket
// 初始化一個 WebSocket 對象 var ws = new WebSocket("ws://localhost:9998/echo"); // 創建 web socket 鏈接成功觸發事件 ws.onopen = function () { // 使用 send() 方法發送數據 ws.send("發送數據"); alert("數據發送中..."); }; // 接收服務端數據時觸發事件 ws.onmessage = function (evt) { var received_msg = evt.data; alert("數據已接收..."); }; // 斷開 web socket 鏈接成功觸發事件 ws.onclose = function () { alert("鏈接已關閉..."); };
服務器端的就主要用代碼來實現吧
源碼地址 密碼:f28e
服務端獲取消息很簡單,主要是向服務器端發送消息。須要向客戶端發送消息,那麼咱們須要知道客戶端的某個惟一標識,那麼這個標識用什麼來表示呢,那就是session。
直接貼碼,裏面註釋很清晰 須要的依賴
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>
java源碼
package me.gacl.websocket; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; /** * @ServerEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket服務器端, * 註解的值將被用於監聽用戶鏈接的終端訪問URL地址,客戶端能夠經過這個URL來鏈接到WebSocket服務器端 */ @ServerEndpoint("/websocket") public class WebSocketTest { //靜態變量,用來記錄當前在線鏈接數。應該把它設計成線程安全的。 private static int onlineCount = 0; //concurrent包的線程安全Set,用來存放每一個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通訊的話,可使用Map來存放,其中Key能夠爲用戶標識 private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>(); //與某個客戶端的鏈接會話,須要經過它來給客戶端發送數據 private Session session; /** * 鏈接創建成功調用的方法 * @param session 可選的參數。session爲與某個客戶端的鏈接會話,須要經過它來給客戶端發送數據 */ @OnOpen public void onOpen(Session session){ this.session = session; webSocketSet.add(this); //加入set中 addOnlineCount(); //在線數加1 System.out.println("有新鏈接加入!當前在線人數爲" + getOnlineCount()); } /** * 鏈接關閉調用的方法 */ @OnClose public void onClose(){ webSocketSet.remove(this); //從set中刪除 subOnlineCount(); //在線數減1 System.out.println("有一鏈接關閉!當前在線人數爲" + getOnlineCount()); } /** * 收到客戶端消息後調用的方法 * @param message 客戶端發送過來的消息 * @param session 可選的參數 */ @OnMessage public void onMessage(String message, Session session) { System.out.println("來自客戶端的消息:" + message); //羣發消息 for(WebSocketTest item: webSocketSet){ try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); continue; } } } /** * 發生錯誤時調用 * @param session * @param error */ @OnError public void onError(Session session, Throwable error){ System.out.println("發生錯誤"); error.printStackTrace(); } /** * 這個方法與上面幾個方法不同。沒有用註解,是根據本身須要添加的方法。 * @param message * @throws IOException */ public void sendMessage(String message) throws IOException{ this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message); } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketTest.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketTest.onlineCount--; } }
這裏經過從新寫一個controller,直接給客戶端發送消息,先貼這裏的代碼,應該大部分人都是想實現這個功能。這裏的目的是,在處理一個其餘請求以後,須要給原來的客戶端發送消息,告訴它我已經處理完了,收到消息以後再處理後續的邏輯(掃碼場景比較廣泛)。
@GetMapping("/") public WebsocketResponse sendSuccess(){ MyHandler send = new MyHandler(); TextMessage msg = new TextMessage("發給客戶端"); send.sendMessageToUser("888",msg); return new WebsocketResponse(1); }
有須要的直到源碼中拉取代碼吧,服務器端的原理都相似