一、WebSocket應用場景
通俗的講,WebSocket 是一種伺服器可以主動向用戶端推送資訊,用戶端也可以主動向伺服器發送資訊,實作真正的雙向平等對話的協定。 WebSocket是為了解決HTTP的短連結而産生的一種協定。如果不與http用戶端産生關系,那麼直接用TCP協定即可。無需使用WebSocket協定。
是以,在web用戶端如果實作雙向通信,想要服務端主動往web用戶端推送消息的話,可以考慮使用websocket技術。
二、WebSocket原理
WebSocet是借用了一層HTTP協定,又對TCP協定進一步封裝而成的一種協定。
WebSocket是基于HTTP協定的,借用HTTP協定來完成了一部分握手動作。我們看websocket的請求頭:

我們着重看請求頭的這兩行:
Upgrade: websocket
Connection: Upgrade
這兩行是WebSocket的關鍵。通過這兩行資訊,告訴Apache、Nginx等http伺服器,此協定要用WebSocket技術處理,而不是用HTTP協定處理。WebSocket技術誕生于2008年,至今大多數HTTP伺服器已經支援了該協定。由此可見,WebSocket先通過HTTP協定與伺服器建立聯系,然後通知HTTP伺服器,要按照WebSocket協定來處理。至此,用戶端和服務端就建立了WebSocket長連接配接。
Sec-WebSocket-Key 是一個 Base64 encode 的值,這個是浏覽器随機生成的,是會話的唯一辨別。
我們再看伺服器對WebSocket請求的響應:
這裡就是HTTP協定負責的最後區域,告訴用戶端,已經成功切換為WebSocket協定了。
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept 這個則是經過伺服器确認,并且加密過後的 Sec-WebSocket-Key ,作為身份認證使用。
至此,HTTP 已經完成它所有工作了,接下來就是完全按照 WebSocket 協定進行了。
三、WebSocket特點
- 建立在 TCP 協定之上,伺服器端的實作比較容易。
- 與 HTTP 協定有着良好的相容性。預設端口也是80和443,并且握手階段采用 HTTP 協定,是以握手時不容易屏蔽,能通過各種 HTTP代理伺服器。
- 資料格式比較輕量,性能開銷小,通信高效。
- 可以發送文本,也可以發送二進制資料。
- 沒有同源限制,用戶端可以與任意伺服器通信。
- 協定辨別符是ws(如果加密,則為wss),伺服器網址就是 URL。
四、與HTTP協定差別
Websocket 其實是一個新協定,跟 HTTP 協定基本沒有關系,隻是為了相容現有浏覽器,是以在握手階段使用了 HTTP 。
http協定是短連結,因為請求之後,都會關閉連接配接,下次重新請求資料,需要再次打開連接配接。WebSocket協定是一種長連接配接,隻需要通過一次請求來初始化連結,然後所有的請求和響應都是通過這個TCP連結進行通信。
五、心跳機制
WebSocket是基于TCP協定的,TCP協定自帶keepalive心跳機制。是以WebSocket也有keepalive心跳機制。
TCP的keepalive機制,我們後面研究。使用WebSocket技術,一般我們要自己實作心跳機制。那為什麼不直接用TCP的keepalive機制呢,原因如下:
1.client異常挂死,此時keepalive機制無法回報真實的client狀态;
2.client 異常斷電斷網出現TCP假死keepalive并不能根本性解決問題,實際上網際網路環境很不穩定;3.ws在應用層,基于傳輸層,在ws中操作TCP也很不友善。封裝就意味着易用性提高靈活性降低。
是以我們在應用層開啟心跳。(上面的原因,我們在研究TCP的keepalive後再做體會)。應用層頻率為1次/10mins。
六、斷開重連處理機制
用戶端正常/異常斷開處理機制:
服務端正常/異常斷開處理機制:
server斷電斷網時client如何感覺:
補充一下中間線路斷網情況:
如:中間nat裝置斷網(網際網路環境中間nat裝置是非常多的)或者server網絡斷開.這裡大家注意client斷網不算是中間線路斷網,因為client端斷網應用程式馬上可以感覺.但是client所在區域網路的出口nat斷開的就算是中間網絡斷開.
其實上邊已經提到了server網絡斷開的情況,分别說明了server和client各自的檢測辦法.但是很多網絡不穩定的情況,如:斷開18分鐘後網絡又恢複了,這裡涉及到一個重連機制,首先大家要明白當中間網絡斷開時實際上是兩段各自維護本端tcp的.最終會觸發tcp強制拆鍊(不發送四次揮手).分為兩種情況讨論:
(1)網絡恢複時,client已經将自己連接配接斷開了,但是server認為網絡還在連接配接中,和tcp假死很像.這種情況在服務端檢測心跳逾時之前,服務端推送消息是沒有辦法到達用戶端的.但是這時服務端的試圖發消息動作會觸發服務端發現這個連接配接已經斷開了. 從現象看ws重連時間為: 網絡恢複時間——>server發現連接配接斷開(server發消息)+逾時/server心跳檢測逾時 (前提:網絡斷開後到網絡恢複中間這段時間server沒法過消息給client,如果發送過可能網絡連接配接上立即觸發服務端發現連接配接斷開.)
(2)網絡恢複時,client沒有将自己連接配接斷開,但是server已經斷開.這種情況在client下一次心跳發送後會觸發tcp重發,重發一定時間沒有回複client也會進行強制拆鍊.ws重連時間為:網絡恢複時間——>client下一次心跳時間+逾時. (前提:網絡斷開後到網絡恢複中間這段時間client沒發過心跳給server,如果發送過可能網絡連接配接上立即觸發用戶端發現連接配接斷開.)
上邊兩個前提有點難懂,意思是當網絡斷開到網絡恢複中間這段時間發送過消息,那麼這個消息第一次發送肯定是到不了對端,但是這時就已經開始tcp重傳機制了,可能網絡恢複時恰好有一次重傳,你的消息可以發到對端了,但是對端tcp端口已經關閉,tcp發生異常也就立即觸發了本端tcp的關閉.
綜上:tcp重連是需要時間的,這個時間肯定是越短越好,但是又不能太短,這個時間的确定大家可以參考本篇最後的測試.