WebSocket協議支持(在受控環境中運行不受信任的代碼的)客戶端與(選擇加入該代碼的通訊的)遠程主機之間進行全雙工通訊。用於此的安全模型是Web瀏覽器經常使用的基於原始的安全模式。 協議包括一個開放的握手以及隨後的TCP層上的消息幀。 該技術的目標是爲基於瀏覽器的、須要和服務器進行雙向通訊的(服務器不能依賴於打開多個HTTP鏈接(例如,使用XMLHttpRequest或<iframe>和長輪詢))應用程序提供一種通訊機制。javascript
在實現websocket連線過程當中,須要經過瀏覽器發出websocket連線請求,而後服務器發出迴應,這個過程一般稱爲「握手」 。在 WebSocket API,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。在此WebSocket 協議中,爲咱們實現即時服務帶來了兩大好處:java
1. Headerweb
互相溝通的Header是很小的-大概只有 2 Bytesspring
2. Server Push瀏覽器
服務器的推送,服務器再也不被動的接收到瀏覽器的請求以後才返回數據,而是在有新數據時就主動推送給瀏覽器。安全
在HTML5中內置有一些API,用於響應應用程序發起的請求。基本API語句以下: 建立對象 1 var ws = new WebSocket(url,name); url爲WebSocket服務器的地址,name爲發起握手的協議名稱,爲可選擇項。 發送文本消息 ws.send(msg); msg爲文本消息,對於其餘類型的能夠經過二進制形式發送。 接收消息 ws.onmessage = (function(){...})(); 錯誤處理 ws.onerror = (function(){...})(); 關閉鏈接 ws.close();
在pom文件中引入WebSocket相關jar包服務器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
/** * WebSocket 配置 */ @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
import cn.hutool.core.collection.CollectionUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.annotation.Nullable; import javax.annotation.Resource; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; /** * WebSocket 服務器 */ @Component @Slf4j @ServerEndpoint("/ws/{userId}") public class WebSocketServer { private static final AtomicInteger OnlineCount = new AtomicInteger(0); // concurrent包的線程安全Set,用來存放每一個客戶端對應的Session對象。 private static CopyOnWriteArraySet<WebSocketServer> socketServers = new CopyOnWriteArraySet<WebSocketServer>(); //用戶id private Long userId; //當前會話 private Session session; /** * 鏈接創建成功調用的方法 */ @OnOpen public void onOpen(@PathParam("userId")Long userId, Session session) { socketServers.add(this); this.session=session; System.out.println(session.toString()); this.userId=userId; int cnt = OnlineCount.incrementAndGet(); // 在線數加1 log.info("有鏈接加入,當前鏈接數爲:{}", cnt); try { sendMessage("鏈接服務器成功"); } catch (IOException e) { log.error("鏈接IO異常:{}",e.getMessage()); } } /** * 鏈接關閉調用的方法 */ @OnClose public void onClose(Session session) { socketServers.remove(this); int cnt = OnlineCount.decrementAndGet(); log.info("有鏈接關閉,當前鏈接數爲:{}", cnt); } /** * 收到客戶端消息後調用的方法 * * @param message 客戶端發送過來的消息*/ @OnMessage public void onMessage(String message, Session session) { //羣發消息 for (WebSocketServer item : socketServers) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * 發送消息,實踐代表,每次瀏覽器刷新,session會發生變化。 * @param message */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 出現錯誤 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("發生錯誤:{},Session ID: {}",error.getMessage(),session.getId()); error.printStackTrace(); } /** * 給某個具體的用戶推送消息 * @param message * @param userId * @throws IOException */ public void sendMessageTo(@Nullable String message,@Nullable Long userId){ for(WebSocketServer item :socketServers){ if(item.userId.equals(userId)){ try { item.session.getBasicRemote().sendText(message); } catch (IOException e) { log.error("推送消息:{} 給用戶{}出現IO異常",message,userId); } } } } }
//登陸成功後 進入主頁面,鏈接服務器websocket 接收服務器推送過來的消息 connectWebSocket(userId); function connectWebSocket(){ var websocket = null; //判斷當前瀏覽器是否支持webSocket if('WebSocket' in window){ websocket = new WebSocket("ws://127.0.0.1:8080/ws/"+userId); }else{ alert("您的瀏覽器不支持網頁消息推送"); } // 連接發生錯誤的回調方法 websocket.onerror = function(){ console.log("error"); } // 連接成功創建的回調方法 websocket.onopen = function(event){ console.log("open"); } // 接受到消息的回調方法 websocket.onmessage = function(event){ //在這裏將服務器推送過來的消息 根據本身的業務進行處理 console.log(event.data); } // 連接關閉的回調方法 websocket.onclose = function(){ console.log("close"); } //監聽窗口關閉事件,當窗口關閉時,主動關閉wesocket 連接,防止連接還沒斷開就關閉窗口,server端會拋異常。 window.onbeforeunload = function(){ websocket.close(); } // 關閉連接 function closeWebSocket(){ websocket.close(); } }