一.為什麼要有WebSocket
servlet,tomcat底層核心思路都是Socket
HTML頁面在剛剛開始出現的時候是靜态的,不能夠進行互動,後來有了JavaScript,在一
定程度上解決了這個問題,但是JavaScript剛出現的時候并不能和服務端進行互動,直到Ajax的出現。
Ajax有效的解決了頁面和服務端進行互動的問題,不過Ajax有一個問題,就是所有的請求都必須由客戶
端發起,服務端進行響應,如果服務端有最新的消息,難以即時的發送到用戶端去,在WebSocket技術
出現之前,為了讓用戶端能夠即時的擷取服務端的資料,一般采用如下三種方案
1.1 輪詢
最簡單的一種解決方案, 就是用戶端在固定的時間間隔下(一般是1秒)不停的向伺服器端發送請求,檢視服務端是否有最新的資料,服務端如果有最新的資料則傳回給用戶端,服務端如果沒有則傳回一個空的json或者xml文檔,這種方式的實作起來簡單,但是弊端也很明顯,就是會有大量的無效請求,服務端的資源被大大的浪費了
1.2 長輪詢
長連接配接有點類似于輪詢,不同的是服務端不是每次都會響應用戶端的請求,隻有在服務端有最新資料的時候才會響應用戶端的請求,這種方式很明顯會節省網絡資源和服務端資源,但是也存在一些問題,比如:
1.如果浏覽器在伺服器響應之前有新資料要發送就隻能建立一個新的并發請求,或者先嘗試斷掉目前請求然後再建立新的請求。
2.TCP和HTTP規範中都有連接配接逾時一說,是以所謂的長連接配接并不能一直持續,服務端和用戶端的連接配接需要定期的連接配接和關閉再連接配接,當然也有一些技術能夠延長每次連接配接的時間,這是題外話。
1.3Applet和Flash
Applet和Flash都已經是明日黃花了,不過這兩個技術在當年除了以讓我們的HTML頁面更加絢麗之外,還可以解決消息推送問題Ajax這種技術去實作全雙工通信已經陷入困境的時候,開發者圖Apple和Flash來模拟全雙工通信,開發者可以建立一個隻有像素點大的普通透明的Applet或者Flash,然後将之内嵌在頁中, 然後這Apple或者Flash中的代碼建立出一個Socket連接配接種連接配接方式消除了HTT協定中的各種限制,當伺服器有消息發到用戶端的時候,開發者以在Applet或者Flash中調用JavaScript函數,并将伺服器傳來的消傳遞給JavaScript函數,然後更新頁面,當浏覽器有資料要發送給務器的時候,也一樣,通過Applet或者Flash來傳遞。這種方式真正實作了全雙工通信,不過也有問題,如下:
1.浏覽器必須能夠運作Java或者Flash
2.無論是Applet還是Flash都存在安全問題
3.随着HTML5在标準在浏覽器中廣泛支援,Flash下架已經被提上日程
1.4WebSocket的特點

所有的HTTP用戶端(浏覽器、移動端等)都可以在請求頭中包Connection:Upgrade,這個表示用戶端希望更新請求協定,那麼希望更新成什麼樣的協定呢?我們需要在Upgrade頭中指定一個或多個協定的清單,當然這些協定必須相容HTTP/1.1協定。伺服器收到請求之後,如果接受更新請求,那麼将會傳回一個101的狀态碼表示轉換請求協定,同時在響應的Upgrade頭中使用單個值,這個個值就是請求協定清單中伺服器支援的第一個協定(即請求頭Upgrade字段中列出來的協定清單中伺服器支援的第一個協定)。HTTP更新最大的好處是最終使我們可以使用任意的協定,在升握手完成之後,它就不再使用HTTP連接配接了,我們甚至可以在更新握手完成之後建立一個Socket連接配接,理論上我們可以使用HTTP升在兩個端點之間使用任何自己設計的協定,進而建立出各種各樣TCP通信,當然浏覽器不會讓開發者随意去這麼做,而是要指定某些協定,WebSocket應運而生!
1.5 WebSocket的用途
說了這麼多那麼WebSocket協定到底可以用在哪些地方呢?事實上,WebSocket協定的用途幾乎是沒有限制的,比如
1.網頁上的線上聊天
2.多人線上遊戲
3.線上股票網站
4.線上即時新聞網站
5.高清視訊流
6.應用叢集之間的通信
7.遠端系統/軟體的狀态和性能的實時監控
五子棋小遊戲
線上聊天
1.6 WebSocket的優勢
1.由于WebSocket連接配接在端口80(ws)或者443(wss)上建立,HTTP使用的端口相同,這樣,基本上所有的防火牆都不會阻塞WebSocket連接配接
2.WebSocket使用HTTP協定進行握手,是以它可以自然而然的成到網絡浏覽器和HTTP伺服器中
3.心跳消息(ping和pong)将被反複的發送,進而保持WebSocket連接配接幾乎一直處于活躍狀态。一般來說是這樣,一個節點周期性的一個小資料包到另外一個節點(ping),而另一個節點則使用了包含相同資料的資料包作為應(pong),這樣兩個節點都将處于連接配接狀态
4.使用該協定,當消息啟動或者到達的時候,服務端和用戶端都以知道
5.WebSocket連接配接關閉時将發送一個特殊的關閉消息
6.WebSocket支援跨域,可以避免Ajax的限制
7.HTTP規範要求浏覽器将并發連接配接數限制為每個主機名兩個接是當我們使用WebSocket的時候,當握手完成之後該限制就不在了因為此時的連接配接已經不再是HTTP連接配接了
二. WebSocket的實際案例
2.1 加入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.2實體類
package com.huang.vhr.framework.web.entity;
public class Chat {
private String from;
private String to;
private String msg;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
2.3 配置類WebSocketConfig
package com.huang.vhr.framework.comments.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
//開啟消息代理
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 配置 websocket 連接配接位址
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/myws/ep")
//設定允許的域
.setAllowedOrigins("http://localhost:8080")
//支援前端使用 SockJS
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue");
}
}
2.4controller層調用底層方法
package com.huang.vhr.controller.system.basic;
import com.huang.vhr.framework.web.entity.Chat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.security.Principal;
@Controller
public class ChatController {
@Autowired
SimpMessagingTemplate simpMessagingTemplate;
/**
*
* @param chat
* @param principal 這個就是目前登入使用者對象
*/
@MessageMapping("/myws/chat")
public void chat(Chat chat, Principal principal) {
chat.setFrom(principal.getName());
//發送一條消息
simpMessagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);
}
}
2.5 前端–vue
不會前端。。
留着先吧