WebSocket是一種比較新的協議,它是伴隨著html5
規範而生的,雖然還比較年輕,但大多主流瀏覽器都已經支援。它使用方面、應用廣泛,已經滲透到前後端開發的各種場景中。
對http一問一答
中二式流程的不滿,催生了支援雙向通訊的WebSocket
誕生。WebSocket是個不太乾淨
協議。
一、WebSocket協議只能瀏覽器發起麼?
不是。目前此協議的受眾的也不僅僅是web開發者。
WebSocket只是一種協議,它和http協議一樣,使用類似okhttp
的元件,可以在任何地方進行呼叫,甚至可以藉助WebSocket實現RPC
框架。
二、WebSocket和HTTP什麼關係?
WebSocket和http一樣,都是處於OSI
模型中的最高層:應用層
。
http
協議進行握手,握手成功後,就會變身為TCP通道
,從此與http不再相見。
使用netstat或者ss,能夠看到對應的連線,它與處於抽象層的socket,在外觀上沒有區別。
三、WebSocket和長輪詢有什麼區別?
長輪詢,就是客戶端傳送一個請求,服務端將一直在這個連線上等待(當然有一個超長的超時時間),直到有資料才返回,它依然是一個一問一答的模式。比如著名的comted。
WebSocket在握手成功後,就是全雙工
的TCP通道,資料可以主動從服務端傳送到客戶端,處於連結兩端的應用沒有任何區別。
WebSocket建立的連線和Http的長連線是不一樣的。由於Http長連線底層依然是Http協議,所以它還是一問一答,只是Hold住了一條命長點的連線而已。
長輪詢和Http長連線是阻塞的I/O,但WebSocket可以是非阻塞的(具體是多路複用)。
四、如何建立一個連線?
WebSocket的連線建立是藉助Http協議進行的。這樣設計主要是考慮相容性,在瀏覽器中就可以很方便的發起請求,看起來比較具有迷惑性。
下圖是一個典型的由瀏覽器發起的ws請求,可以看到和http請求長的是非常相似的。但是,它只是請求階段長得像而已:
請求的地址,一般是:ws://\*\*\*
,或者是使用了SSL/TLS加密的安全協議wss:
,用來標識是WebSocket請求。
1、 首先,通過Http頭裡面的Upgrade
域,請求進行協議轉換。如果服務端支援的話,就可以切換到WebSocket協議。簡單點講:連線已經在那了,通過握手切換成ws協議,就是切換了連線的一個狀態而已。
1、Connection
域可以認為是與Upgrade
域配對的頭資訊。像nginx等代理伺服器,是要先處理Connection,然後再發起協議轉換的。
Sec-WebSocket-Key 是隨機的字串,伺服器端會用這些資料來構造出一個 SHA-1 的資訊摘要。如此操作,可以儘量避免普通 HTTP 請求被誤認為 WebSocket 協議。
其他的,像Sec-WebSocket*字樣的頭資訊,表明了客戶端支援的子協議以及其他資訊。像loT中很流行的mqtt,就可以作為WebSocket的子協議。
使用javascript,可以很容易連線一個WebSocket服務端。<script>
var ws = new WebSocket('ws://localhost:80');
ws.onopen = function () {
console.log('ws onopen');
ws.send('from client: hello');
};
ws.onmessage = function (e) {
console.log('ws onmessage');
console.log('from server: ' + e.data);
};
...
</script>
複製程式碼
五、如何處理資料?
WebSocket是通過事件通知的方式執行的。它包含四個事件和兩個動作(傳送和關閉)。
WebSocket的事件
事件 | 鉤子 | 備註 |
---|---|---|
open | onopen | 連線建立時觸發 |
message | onmessage | 客戶端接收服務端資料時觸發 |
error | onerror | 通訊發生錯誤時觸發 |
close | onclose | 連線關閉時觸發 |
資料可直接通過Socket.send()
方法進行傳輸。
通過chrome的Inspect->Network->WS,可以看到頁面上的WebSocket連線。如圖Opcode為2,表明它是一個二進位制幀。
WebSocket有類似tcp協議的幀格式,在此不做過多解釋。參考:(tools.ietf.org/html/rfc645…)
心跳
心跳對應的ping、pong操作,opcode分別是0x9、0xA。收到心跳的一方需要自行更新心跳的更新時間。同使用Netty,我們到底在開發些什麼?介紹的類似,在一些移動環境中,需要更加智慧的控制心跳。
六、如何使用Nginx做負載均衡?
nginx官網已經給出了例子。主要是Upgrade和Connection頭的設定。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
location /chat/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
複製程式碼
需要注意的是,nginx做負載均衡,不需要配置ip_hash
等引數,nginx天然支援。由於ip_hash僅使用ip地址的前三個數字做hash,還有可能造成服務端的不均衡。
七、java服務端怎麼實現?
可以實現javax.WebSocket下的包,簡單的實現ws服務端。目前基本可以通過註解的方式去編寫程式碼,比如ServerEndpoint
。
推薦使用基於netty的netty-socketio進行服務端的編寫。由於使用的是netty,所以能夠在多個層面進行切入,獲取一些統計資料,執行一些控制指令。socketio是一套解決方案,它有多個語言的客戶端,並處理了市面上大多數的相容問題。
八、WebSocket能幹些啥?
通知功能
保持一個長連線,當服務端遊新的訊息,能夠實時的推送到使用方。像知乎的點贊通知、評論等,都可以使用WebSocket通訊。
某些使用H5
的客戶端,為了簡化開發,也會使用WebSocket進行訊息的通知,由於它是實時推送的,會有更好的使用者體驗。
資料收集
一些次優級別的資料,比如行為日誌、trace、異常執棧收集等,都可以開闢專門的WebSocket通道進行傳輸。這能夠增加資訊的集中度,並能及時的針對使用者的行為進行合適的配置推送。由於大多數瀏覽器核心都支援,它將使客戶端APM
程式設計模型變得簡單。
加密 && 認證
雖然使用Fiddler、Charles等能夠抓到很多WebSocket包。但如果同時開啟SSL,傳輸加密後的二進位制資料,會大幅增加破解的成本,會安全的多。
反向控制鉤子
這個...由於是雙工長連線,服務端完全可以推送一些鉤子命令,甚至直接是程式碼,在客戶端進行執行。比如截個屏,錄個音,種個小馬。使用者只要通過了授權申請,剩下的就隨你發揮了。
支付寶偷偷呼叫你的相機給你拍照的梗,我是相信的。
End
想當年,cometd的出現,驚為天人,振奮了很久。但技術日新月異,cometd已經衰老,而Socket.io得到了快速發展。WebSocket經過一段時間的混沌期,規範已經越來越完善,使用也越來越方便,不需要再處理那麼多的相容。
但它的本質,還是新瓶裝舊酒,換湯不換藥。WebSocket的發展得益於HTML5規範的制定。規範的意義,就是約束廠商們天馬行空的實現,以及指明發展的方向。
這當然有典型的反例,那就是ie
。現在,只有一群公認的**,還堅持在用。