??我們經常用的是HTTP協議,而HTTP協議是一種無狀態的協議,要實現有狀態的會話必須借助一些外部機制如session和cookie,這或多或少會帶來一些不便,尤其是服務端和客戶端需要實時交換數據的時候(監控,聊天),這個問題更加明顯。為了適應這種環境,websocket就產生了。
??是一種在單個TCP連接上進行全雙工通信的持久化協議。
允許服務端主動向客戶端推送數據(允許服務端主動向客戶端推送數據)
在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。
聊天:比如我們常用的兩天軟件,就用到了websocket
監控:實時監控一個網站的在線
總之適用于:客戶端與服務器段高頻率低延遲交換事件。
更強的實時性
保持連接狀態,創建一次連接后,之后通信時可以省略部分狀態信息
較少的控制開銷,在連接創建后,服務器和客戶端之間交換數據時,用于協議控制的數據包頭部相對較小。在不包含擴展的情況下,對于服務器到客戶端的內容,此頭部大小只有2至10字節(和數據包長度有關);對于客戶端到服務器的內容,此頭部還需要加上額外的4字節的掩碼。相對于HTTP請求每次都要攜帶完整的頭部,此項開銷顯著減少了
由于websocket使用的持久連接,與服務器一直保持連接,對服務器壓力很大
??只需建立一次Request/Response消息對,之后都是TCP連接,避免了需要多次建立Request/Response消息對而產生的冗余頭部信息。Request/Response需要三次w
瀏覽器發送websocket請求頭類似如下:
Accept-Encoding:瀏覽器可以接受的數據的壓縮類型。
Accept-Language:瀏覽器可以接受的語言類型。
Cache-Control:no-cache不使用強緩存。
Connection:Upgrade 通知服務器通信協議提升。
Host:主機名。
Origin:用于驗證瀏覽器域名是否在服務器許可范圍內。
Pragma:no-cache HTTP/1.0定義的不使用本地緩存。
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Sec-WebSocket-Key:lb69kw8CsB4CrSk9tKa3 g==
握手協議密鑰,base64編碼的16字節的隨機字符串。
Sec-WebSocket-Version:13 版本號。
Upgrade:websocket 使用websocket協議進行傳輸數據,而不使用HTTP/1.1。
User-Agent:用戶代理字符串。
服務器接收到客戶端請求后做出響應并返回如下:
下面是服務器返回的頭部解釋:
Connection:Upgrade 通信協議提升。
Date:通信時間
Upgrade: websocket 傳輸協議升級為websocket。
Sec-WebSocket-Extensions:permessage-deflate
Sec-WebSocket-Accept:q9g5u1WfIWaAjNgMmjlTQTqkS/k=
將Sec-WebSocket-Key的值進行一定的運算和該值進行比較來判斷是否是目標服務器響應了WebSocket請求。
Upgrade: 使用websocket協議進行數據傳輸
這里我所指的客戶端通常是指瀏覽器,而且都支持WebSocket API。你也可以通過window.WebSocket來判斷你的瀏覽器是否支持WebSocket,
var ws = null;function connect(){ // 1. 創建websocket實例 if ('WebSocket' in window){ ws = new WebSocket("ws://127.0.0.1:8086/socketServer/" $("#username").val()); } else if ('MozWebSocket' in window){ ws = new MozWebSocket("ws://127.0.0.1:8086/socketServer/" $("#username").val()); } else{ alert("該瀏覽器不支持websocket"); } // 2. 新建session,即創建連接時觸發此方法 ws.onopen = function() { // 可以在此寫創建會話后想實現的的邏輯 }; // 3. 接受消息,evt 是接收到服務端返回給客戶端的數據 ws.onmessage = function(evt) { // 可以在接受消息后想實現的的邏輯,比如修改頁面上顯示在線認數 }; // 4. 關閉連接 ws.onclose = function() { // 可以在此寫會話關閉后想實現的的邏輯 }; // 5. 出現錯誤 ws.onerror = function (e) { // 可以在此寫出現錯誤后想實現的的邏輯 };} // 客戶端給服務端發送消息時,調用此方法function sendMsg() { // 調用ws的send()事件 ws.send(發送的內容);}
解釋:
首先要創建webSocket的實例,url一定是ws形式
然后給ws綁定open,message,error,close四個事件
發送數據使用send方法,該方法除了可以發送字符串以外還可以發送blob和二進制數據
創建好的ws對象有一個readyState屬性,它的取值有0,1,2,3分別表示正在連接,連接成功,正在關閉,連接關閉
第一步:引依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <-- 使用springboot的內置tomcat時,就不需要引入javaEE-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency>
第二步:啟用websocket的支持
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
第三步:通信的具體實現:
具體實現可以參照如下例子,實現了一個簡單的聊天功能:鏈接:https://pan.baidu.com/s/1iTjETNwQrICfHyDDh5yicg 提取碼:0el5 ,可以參考下面這篇博客:https://blog.csdn.net/moshowgame/article/details/80275084
簡單說一下聊天實現的邏輯:每次客戶端創建連接時會產生一個會話session,并存放起來,該session唯一標識,當客戶端發送消息時,會給服務端發送包含發送給誰及發送內容的信息的消息message,然后客戶端根據客戶端傳過來的發送給誰的信息查詢對應的session,然后調用session.getBasicRemote().sendText(message)就可以給對方發送消息了,對方會監聽ws.onmessage()方法,該方法就是監聽對方發消息的,只要對方發消息就會觸發該方法然后接受消息,如果時群發,循環調用上面的步驟即可
??三次握手詳解參見:https://www.jianshu.com/p/85aef5a0e3d8,
??四次揮手詳解參見:https://www.cnblogs.com/qdhxhz/p/8470997.html
都屬于應用層協議
都是用Request/Response模型進行連接的簡歷
都可以在網絡中傳輸數據
ws使用HTTP來建立連接,但是定義了一系列新的header域,這些域在HTTP中并不會使用
ws連接建立之后,通信雙方都可以在任何時刻向另一方發送數據
ws連接建立之后即握手后,TCP套接字保持打開狀態(套接字是網絡編程中的一種通信機制,是支持TCP/IP的網絡通信的基本操作單元,可以看做是不同主機之間的進程進行雙向通信的端點,簡單的說就是通信的兩方的一種約定,用套接字中的相關函數來完成通信過程),數據的傳輸使用幀來傳遞,不再需要Request消息
ws的數據幀有序
??說到這個我不得不用這句話來回答你:就像Java和JavaScript的關系一樣,兩者沒有絲毫的聯系。socket即通常說的套接字,它是和網絡中的運輸層打交道的
來源:http://www.icode9.com/content-4-188051.html