WebSocket跟常規的http協議的區別和優缺點這裡大概描述一下 一、websocket與http http協議是用在應用層的協議,他是基於tcp協議的,http協議建立鏈接也必須要有三次握手才能發送信息。http鏈接分為短鏈接,長鏈接,短鏈接是每次請求都要三次握手才能發送自己的信息。即每一個r ...
WebSocket跟常規的http協議的區別和優缺點這裡大概描述一下
一、websocket與http
http協議是用在應用層的協議,他是基於tcp協議的,http協議建立鏈接也必須要有三次握手才能發送信息。http鏈接分為短鏈接,長鏈接,短鏈接是每次請求都要三次握手才能發送自己的信息。即每一個request對應一個response。長鏈接是在一定的期限內保持鏈接。保持TCP連接不斷開。客戶端與伺服器通信,必須要有客戶端發起然後伺服器返回結果。客戶端是主動的,伺服器是被動的。
WebSocket是HTML5中的協議, 他是為瞭解決客戶端發起多個http請求到伺服器資源瀏覽器必須要經過長時間的輪訓問題而生的,他實現了多路復用,他是全雙工通信。在webSocket協議下客服端和瀏覽器可以同時發送信息。
二、HTTP的長連接與websocket的持久連接
HTTP1.1的連接預設使用長連接(persistent connection),
即在一定的期限內保持鏈接,客戶端會需要在短時間內向服務端請求大量的資源,保持TCP連接不斷開。客戶端與伺服器通信,必須要有客戶端發起然後伺服器返回結果。客戶端是主動的,伺服器是被動的。
在一個TCP連接上可以傳輸多個Request/Response消息對,所以本質上還是Request/Response消息對,仍然會造成資源的浪費、實時性不強等問題。
如果不是持續連接,即短連接,那麼每個資源都要建立一個新的連接,HTTP底層使用的是TCP,那麼每次都要使用三次握手建立TCP連接,即每一個request對應一個response,將造成極大的資源浪費。
長輪詢,即客戶端發送一個超時時間很長的Request,伺服器hold住這個連接,在有新數據到達時返回Response
websocket的持久連接 只需建立一次Request/Response消息對,之後都是TCP連接,避免了需要多次建立Request/Response消息對而產生的冗餘頭部信息。
Websocket只需要一次HTTP握手,所以說整個通訊過程是建立在一次連接/狀態中,而且websocket可以實現服務端主動聯繫客戶端,這是http做不到的。
三、websocket實現
springboot集成websocket項目目錄:
pom添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
因涉及到js連接服務端,所以也寫了對應的html,這裡集成下thymeleaf模板,前後分離的項目這一塊全都是前端做的
<!-- springboot集成thymeleaf的起步依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
properties配置文件
#服務埠號
server.port=8080
server.servlet.context-path=/websocketdemo
#視圖層控制
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html
#spring.mvc.static-path-pattern=/static/**
#關閉thymeleaf的緩存,不然在開發過程中修改頁面不會立刻生效需要重啟,生產可配置為true
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
自定義WebSocketServer,使用底層的websocket方法,提供對應的onOpen、onClose、onMessage、onError方法
添加webSocketConfig配置類
//開啟WebSocket支持 @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
添加webSocketServer服務端類
/** * @ServerEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket伺服器端, * 註解的值將被用於監聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket伺服器端 */ @ServerEndpoint("/websocket/{sid}") @Component public class WebSocketServer { // 靜態變數,用來記錄當前線上連接數。應該把它設計成線程安全的。 private static int onlineCount = 0; // concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。 private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); // 與某個客戶端的連接會話,需要通過它來給客戶端發送數據 private Session session; // 接收sid private String sid = ""; /** * 連接建立成功調用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("sid") String sid) { this.session = session; webSocketSet.add(this); // 加入set中 addOnlineCount(); // 線上數加1 System.out.println("有新視窗開始監聽:" + sid + ",當前線上人數為" + getOnlineCount()); this.sid = sid; try { sendMessage("連接成功"); } catch (IOException e) { System.out.println("websocket IO異常"); } } /** * 連接關閉調用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); // 從set中刪除 subOnlineCount(); // 線上數減1 System.out.println("有一連接關閉!當前線上人數為" + getOnlineCount()); } /** * 收到客戶端消息後調用的方法 * * @param message 客戶端發送過來的消息 */ @OnMessage public void onMessage(String message, Session session) { System.out.println("收到來自視窗" + sid + "的信息:" + message); // 群發消息 for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { System.out.println("發生錯誤"); error.printStackTrace(); } /** * 實現伺服器主動推送 */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 群發自定義消息 */ public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException { System.out.println("推送消息到視窗" + sid + ",推送內容:" + message); for (WebSocketServer item : webSocketSet) { try { // 這裡可以設定只推送給這個sid的,為null則全部推送 if (sid == null) { item.sendMessage(message); } else if (item.sid.equals(sid)) { item.sendMessage(message); } } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() { return webSocketSet; } }
添加對應的controller
@Controller public class websocketController { //跳轉到websocket界面 @RequestMapping(value="/websocket") public String websocket(Model model) { return "websocket"; } }
提供websocket.html頁面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>websocketDemo</title> </head> <body> Welcome<br/> <input id="text" type="text" /> <button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button> <div id="message"></div> <script type="text/javascript"> var websocket = null; //判斷當前瀏覽器是否支持WebSocket if('WebSocket' in window){ websocket = new WebSocket("ws://localhost:8080/websocketdemo/websocket/chat"); }else{ alert('Not support websocket'); } //連接發生錯誤的回調方法 websocket.onerror = function(){ setMessageInnerHTML("error"); }; //連接成功建立的回調方法 websocket.onopen = function(event){ setMessageInnerHTML("open"); }; //接收到消息的回調方法 websocket.onmessage = function(){ setMessageInnerHTML(event.data); }; //連接關閉的回調方法 websocket.onclose = function(){ setMessageInnerHTML("close"); }; //監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉視窗,server端會拋異常。 window.onbeforeunload = function(){ websocket.close(); }; //將消息顯示在網頁上 function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //關閉連接 function closeWebSocket(){ websocket.close(); } //發送消息 function send(){ var message = document.getElementById('text').value; websocket.send(message); } </script> </body> </html>
效果圖: