小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

一文吃透 WebSocket 原理

 天選小丑 2022-01-04
作者:Gaby 原文鏈接 https:///post/7020964728386093093
一.前言

踩著年末的尾巴,提前布局來年,為來年的工作做個(gè)好的鋪墊,所以就開始了面試歷程,因?yàn)轫?xiàng)目中使用到了 WebSocket ,面試官在深挖項(xiàng)目經(jīng)驗(yàn)的時(shí)候,也難免提到 WebSocket 相關(guān)的知識(shí)點(diǎn),因?yàn)橹安]有考慮這么深,所以,回答的還是有所欠缺,因此,趕緊趁熱再熟悉熟悉,也借此機(jī)會(huì),整理出來供大家咀嚼,每個(gè)項(xiàng)目都有其值得挖掘的閃光點(diǎn),要用有愛的眼睛去發(fā)現(xiàn)。

二.什么是 WebSocket

WebSocket 是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。Websocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。

在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接, 并進(jìn)行雙向數(shù)據(jù)傳輸。(維基百科)

WebSocket 本質(zhì)上是一種計(jì)算機(jī)網(wǎng)絡(luò)應(yīng)用層的協(xié)議,用來彌補(bǔ) http 協(xié)議在持久通信能力上的不足。

WebSocket 協(xié)議在2008年誕生,2011年成為國(guó)際標(biāo)準(zhǔn)?,F(xiàn)在最新版本瀏覽器都已經(jīng)支持了。

它的最大特點(diǎn)就是,服務(wù)器可以主動(dòng)向客戶端推送信息,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息,是真正的雙向平等對(duì)話,屬于服務(wù)器推送技術(shù)的一種。

WebSocket 的其他特點(diǎn)包括:

  • (1)建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易。
  • (2)與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種 HTTP 代理服務(wù)器。
  • (3)數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。
  • (4)可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。
  • (5)沒有同源限制,客戶端可以與任意服務(wù)器通信。
  • (6)協(xié)議標(biāo)識(shí)符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是 URL。
ws://:80/some/path

為什么需要 WebSocket?

我們已經(jīng)有了 HTTP 協(xié)議,為什么還需要另一個(gè)協(xié)議?它能帶來什么好處?

因?yàn)?HTTP 協(xié)議有一個(gè)缺陷:通信只能由客戶端發(fā)起,不具備服務(wù)器推送能力。

舉例來說,我們想了解查詢今天的實(shí)時(shí)數(shù)據(jù),只能是客戶端向服務(wù)器發(fā)出請(qǐng)求,服務(wù)器返回查詢結(jié)果。HTTP 協(xié)議做不到服務(wù)器主動(dòng)向客戶端推送信息。

這種單向請(qǐng)求的特點(diǎn),注定了如果服務(wù)器有連續(xù)的狀態(tài)變化,客戶端要獲知就非常麻煩。我們只能使用'輪詢':每隔一段時(shí)候,就發(fā)出一個(gè)詢問,了解服務(wù)器有沒有新的信息。最典型的場(chǎng)景就是聊天室。輪詢的效率低,非常浪費(fèi)資源(因?yàn)楸仨毑煌_B接,或者 HTTP 連接始終打開)。

在 WebSocket 協(xié)議出現(xiàn)以前,創(chuàng)建一個(gè)和服務(wù)端進(jìn)雙通道通信的 web 應(yīng)用,需要依賴HTTP協(xié)議,進(jìn)行不停的輪詢,這會(huì)導(dǎo)致一些問題:

  • 服務(wù)端被迫維持來自每個(gè)客戶端的大量不同的連接
  • 大量的輪詢請(qǐng)求會(huì)造成高開銷,比如會(huì)帶上多余的header,造成了無用的數(shù)據(jù)傳輸。

http 協(xié)議本身是沒有持久通信能力的,但是我們?cè)趯?shí)際的應(yīng)用中,是很需要這種能力的,所以,為了解決這些問題,WebSocket 協(xié)議由此而生,于2011年被IETF定為標(biāo)準(zhǔn)RFC6455,并被RFC7936所補(bǔ)充規(guī)范。并且在 HTML5 標(biāo)準(zhǔn)中增加了有關(guān) WebSocket 協(xié)議的相關(guān) api ,所以只要實(shí)現(xiàn)了 HTML5 標(biāo)準(zhǔn)的客戶端,就可以與支持 WebSocket 協(xié)議的服務(wù)器進(jìn)行全雙工的持久通信了。

WebSocket 與 HTTP 的區(qū)別

WebSocket 與 HTTP 的關(guān)系圖:

  • 相同點(diǎn): 都是一樣基于TCP的,都是可靠性傳輸協(xié)議。都是應(yīng)用層協(xié)議。
  • 聯(lián)系: WebSocket在建立握手時(shí),數(shù)據(jù)是通過HTTP傳輸?shù)?。但是建立之后,在真正傳輸時(shí)候是不需要HTTP協(xié)議的。

下面一張圖說明了 HTTP 與 WebSocket 的主要區(qū)別:

不同點(diǎn):

  • 1、 WebSocket 是雙向通信協(xié)議,模擬 Socket 協(xié)議,可以雙向發(fā)送或接受信息,而 HTTP 是單向的;
  • 2、 WebSocket 是需要瀏覽器和服務(wù)器握手進(jìn)行建立連接的,而 http 是瀏覽器發(fā)起向服務(wù)器的連接。
  • 3、 雖然 HTTP/2 也具備服務(wù)器推送功能,但 HTTP/2 只能推送靜態(tài)資源,無法推送指定的信息。
三、WebSocket協(xié)議的原理

與http協(xié)議一樣, WebSocket 協(xié)議也需要通過已建立的TCP連接來傳輸數(shù)據(jù)。具體實(shí)現(xiàn)上是通過http協(xié)議建立通道,然后在此基礎(chǔ)上用真正 WebSocket 協(xié)議進(jìn)行通信,所以WebSocket協(xié)議和http協(xié)議是有一定的交叉關(guān)系的。首先, WebSocket 是一個(gè)持久化的協(xié)議,相對(duì)于 HTTP 這種非持久的協(xié)議來說。簡(jiǎn)單的舉個(gè)例子吧,用目前應(yīng)用比較廣泛的 PHP 生命周期來解釋。

HTTP 的生命周期通過 Request 來界定,也就是一個(gè) Request 一個(gè) Response ,那么在 HTTP1.0 中,這次 HTTP 請(qǐng)求就結(jié)束了。

在 HTTP1.1 中進(jìn)行了改進(jìn),使得有一個(gè) keep-alive,也就是說,在一個(gè) HTTP 連接中,可以發(fā)送多個(gè) Request,接收多個(gè) Response。但是請(qǐng)記住 Request = Response, 在 HTTP 中永遠(yuǎn)是這樣,也就是說一個(gè) Request 只能有一個(gè) Response。而且這個(gè) Response 也是被動(dòng)的,不能主動(dòng)發(fā)起。首先 WebSocket 是基于 HTTP 協(xié)議的,或者說借用了 HTTP 協(xié)議來完成一部分握手。

首先我們來看個(gè)典型的 WebSocket 握手

GET /chat HTTP/1.1 Host: server. Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://

熟悉 HTTP 的童鞋可能發(fā)現(xiàn)了,這段類似 HTTP 協(xié)議的握手請(qǐng)求中,多了這么幾個(gè)東西。

Upgrade: websocket Connection: Upgrade

這個(gè)就是 WebSocket 的核心了,告訴 Apache 、 nginx 等服務(wù)器:注意啦,我發(fā)起的請(qǐng)求要用 WebSocket 協(xié)議,快點(diǎn)幫我找到對(duì)應(yīng)的助理處理~而不是那個(gè)老土的 HTTP 。

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13

  • 首先, Sec-WebSocket-Key 是一個(gè) Base64 encode 的值,這個(gè)是瀏覽器隨機(jī)生成的,告訴服務(wù)器:泥煤,不要忽悠我,我要驗(yàn)證你是不是真的是 WebSocket 助理。
  • 然后, Sec_WebSocket-Protocol 是一個(gè)用戶定義的字符串,用來區(qū)分同 URL 下,不同的服務(wù)所需要的協(xié)議。簡(jiǎn)單理解:今晚我要服務(wù)A,別搞錯(cuò)啦~
  • 最后, Sec-WebSocket-Version 是告訴服務(wù)器所使用的 WebSocket Draft (協(xié)議版本),在最初的時(shí)候,WebSocket 協(xié)議還在 Draft 階段,各種奇奇怪怪的協(xié)議都有,而且還有很多期奇奇怪怪不同的東西,什么 Firefox 和 Chrome 用的不是一個(gè)版本之類的,當(dāng)初 WebSocket 協(xié)議太多可是一個(gè)大難題。。不過現(xiàn)在還好,已經(jīng)定下來啦~大家都使用同一個(gè)版本:服務(wù)員,我要的是13歲的噢→_→ 然后服務(wù)器會(huì)返回下列東西,表示已經(jīng)接受到請(qǐng)求, 成功建立 WebSocket 啦!
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat

這里開始就是 HTTP 最后負(fù)責(zé)的區(qū)域了,告訴客戶,我已經(jīng)成功切換協(xié)議啦~

Upgrade: websocket Connection: Upgrade

依然是固定的,告訴客戶端即將升級(jí)的是 WebSocket 協(xié)議,而不是 mozillasocket ,lurnarsocket 或者 shitsocket。

然后, Sec-WebSocket-Accept 這個(gè)則是經(jīng)過服務(wù)器確認(rèn),并且加密過后的 Sec-WebSocket-Key。服務(wù)器:好啦好啦,知道啦,給你看我的 ID CARD 來證明行了吧。后面的, Sec-WebSocket-Protocol 則是表示最終使用的協(xié)議。至此,HTTP 已經(jīng)完成它所有工作了,接下來就是完全按照 WebSocket 協(xié)議進(jìn)行了??偨Y(jié), WebSocket 連接的過程是:

  • 首先,客戶端發(fā)起http請(qǐng)求,經(jīng)過3次握手后,建立起TCP連接;http 請(qǐng)求里存放 WebSocket 支持的版本號(hào)等信息,如:Upgrade、Connection、WebSocket-Version等;
  • 然后,服務(wù)器收到客戶端的握手請(qǐng)求后,同樣采用HTTP協(xié)議回饋數(shù)據(jù);
  • 最后,客戶端收到連接成功的消息后,開始借助于TCP傳輸信道進(jìn)行全雙工通信。
四、Websocket的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • WebSocket協(xié)議一旦建議后,互相溝通所消耗的請(qǐng)求頭是很小的
  • 服務(wù)器可以向客戶端推送消息了

缺點(diǎn):

  • 少部分瀏覽器不支持,瀏覽器支持的程度與方式有區(qū)別(IE10)
五、WebSocket應(yīng)用場(chǎng)景
  • 即時(shí)聊天通信
  • 多玩家游戲
  • 在線協(xié)同編輯/編輯
  • 實(shí)時(shí)數(shù)據(jù)流的拉取與推送
  • 體育/游戲?qū)崨r
  • 實(shí)時(shí)地圖位置
  • 即時(shí)Web應(yīng)用程序:即時(shí)Web應(yīng)用程序使用一個(gè)Web套接字在客戶端顯示數(shù)據(jù),這些數(shù)據(jù)由后端服務(wù)器連續(xù)發(fā)送。在WebSocket中,數(shù)據(jù)被連續(xù)推送/傳輸?shù)揭呀?jīng)打開的同一連接中,這就是為什么WebSocket更快并提高了應(yīng)用程序性能的原因。例如在交易網(wǎng)站或比特幣交易中,這是最不穩(wěn)定的事情,它用于顯示價(jià)格波動(dòng),數(shù)據(jù)被后端服務(wù)器使用Web套接字通道連續(xù)推送到客戶端。
  • 游戲應(yīng)用程序:在游戲應(yīng)用程序中,你可能會(huì)注意到,服務(wù)器會(huì)持續(xù)接收數(shù)據(jù),而不會(huì)刷新用戶界面。屏幕上的用戶界面會(huì)自動(dòng)刷新,而且不需要建立新的連接,因此在WebSocket游戲應(yīng)用程序中非常有幫助。
  • 聊天應(yīng)用程序:聊天應(yīng)用程序僅使用WebSocket建立一次連接,便能在訂閱戶之間交換,發(fā)布和廣播消息。它重復(fù)使用相同的WebSocket連接,用于發(fā)送和接收消息以及一對(duì)一的消息傳輸。
六、websocket 斷線重連

心跳就是客戶端定時(shí)的給服務(wù)端發(fā)送消息,證明客戶端是在線的, 如果超過一定的時(shí)間沒有發(fā)送則就是離線了。

如何判斷在線離線?

當(dāng)客戶端第一次發(fā)送請(qǐng)求至服務(wù)端時(shí)會(huì)攜帶唯一標(biāo)識(shí)、以及時(shí)間戳,服務(wù)端到db或者緩存去查詢改請(qǐng)求的唯一標(biāo)識(shí),如果不存在就存入db或者緩存中, 第二次客戶端定時(shí)再次發(fā)送請(qǐng)求依舊攜帶唯一標(biāo)識(shí)、以及時(shí)間戳,服務(wù)端到db或者緩存去查詢改請(qǐng)求的唯一標(biāo)識(shí),如果存在就把上次的時(shí)間戳拿取出來,使用當(dāng)前時(shí)間戳減去上次的時(shí)間, 得出的毫秒秒數(shù)判斷是否大于指定的時(shí)間,若小于的話就是在線,否則就是離線;

如何解決斷線問題

通過查閱資料了解到 nginx 代理的 websocket 轉(zhuǎn)發(fā),無消息連接會(huì)出現(xiàn)超時(shí)斷開問題。網(wǎng)上資料提到解決方案兩種,一種是修改nginx配置信息,第二種是 websocket 發(fā)送心跳包。下面就來總結(jié)一下本次項(xiàng)目實(shí)踐中解決的 websocket 的斷線 和 重連 這兩個(gè)問題的解決方案。主動(dòng)觸發(fā)包括主動(dòng)斷開連接,客戶端主動(dòng)發(fā)送消息給后端

  • 1 主動(dòng)斷開連接
ws.close();

主動(dòng)斷開連接,根據(jù)需要使用,基本很少用到。

  • 2 主動(dòng)發(fā)送消息
ws.send('hello world');
  • 斷線的可能原因1:websocket超時(shí)沒有消息自動(dòng)斷開連接,應(yīng)對(duì)措施:這時(shí)候我們就需要知道服務(wù)端設(shè)置的超時(shí)時(shí)長(zhǎng)是多少,在小于超時(shí)時(shí)間內(nèi)發(fā)送心跳包,有2種方案:一種是客戶端主動(dòng)發(fā)送上行心跳包,另一種方案是服務(wù)端主動(dòng)發(fā)送下行心跳包。

下面主要講一下客戶端也就是前端如何實(shí)現(xiàn)心跳包:

首先了解一下心跳包機(jī)制

跳包之所以叫心跳包是因?yàn)椋核裥奶粯用扛艄潭〞r(shí)間發(fā)一次,以此來告訴服務(wù)器,這個(gè)客戶端還活著。事實(shí)上這是為了保持長(zhǎng)連接,至于這個(gè)包的內(nèi)容,是沒有什么特別規(guī)定的,不過一般都是很小的包,或者只包含包頭的一個(gè)空包。

在 TCP 的機(jī)制里面,本身是存在有心跳包的機(jī)制的,也就是 TCP 的選項(xiàng):SO_KEEPALIVE 。系統(tǒng)默認(rèn)是設(shè)置的2小時(shí)的心跳頻率。但是它檢查不到機(jī)器斷電、網(wǎng)線拔出、防火墻這些斷線。而且邏輯層處理斷線可能也不是那么好處理。一般,如果只是用于?;钸€是可以的。

心跳包一般來說都是在邏輯層發(fā)送空的 echo 包來實(shí)現(xiàn)的。下一個(gè)定時(shí)器,在一定時(shí)間間隔下發(fā)送一個(gè)空包給客戶端,然后客戶端反饋一個(gè)同樣的空包回來,服務(wù)器如果在一定時(shí)間內(nèi)收不到客戶端發(fā)送過來的反饋包,那就只有認(rèn)定說掉線了。

在長(zhǎng)連接下,有可能很長(zhǎng)一段時(shí)間都沒有數(shù)據(jù)往來。理論上說,這個(gè)連接是一直保持連接的,但是實(shí)際情況中,如果中間節(jié)點(diǎn)出現(xiàn)什么故障是難以知道的。更要命的是,有的節(jié)點(diǎn)(防火墻)會(huì)自動(dòng)把一定時(shí)間之內(nèi)沒有數(shù)據(jù)交互的連接給斷掉。在這個(gè)時(shí)候,就需要我們的心跳包了,用于維持長(zhǎng)連接,?;睢?/p>

心跳檢測(cè)步驟:

  • 客戶端每隔一個(gè)時(shí)間間隔發(fā)生一個(gè)探測(cè)包給服務(wù)器
  • 客戶端發(fā)包時(shí)啟動(dòng)一個(gè)超時(shí)定時(shí)器
  • 服務(wù)器端接收到檢測(cè)包,應(yīng)該回應(yīng)一個(gè)包
  • 如果客戶機(jī)收到服務(wù)器的應(yīng)答包,則說明服務(wù)器正常,刪除超時(shí)定時(shí)器
  • 如果客戶端的超時(shí)定時(shí)器超時(shí),依然沒有收到應(yīng)答包,則說明服務(wù)器掛了
// 前端解決方案:心跳檢測(cè) var heartCheck = { timeout: 30000, //30秒發(fā)一次心跳 timeoutObj: null, serverTimeoutObj: null, reset: function(){ clearTimeout(this.timeoutObj); clearTimeout(this.serverTimeoutObj); return this; }, start: function(){ var self = this; this.timeoutObj = setTimeout(function(){ //這里發(fā)送一個(gè)心跳,后端收到后,返回一個(gè)心跳消息, //onmessage拿到返回的心跳就說明連接正常 ws.send('ping'); console.log('ping!') self.serverTimeoutObj = setTimeout(function(){//如果超過一定時(shí)間還沒重置,說明后端主動(dòng)斷開了 ws.close(); //如果onclose會(huì)執(zhí)行reconnect,我們執(zhí)行ws.close()就行了.如果直接執(zhí)行reconnect 會(huì)觸發(fā)onclose導(dǎo)致重連兩次 }, self.timeout); }, this.timeout); } }
  • 斷線的可能原因2:websocket 異常包括服務(wù)端出現(xiàn)中斷,交互切屏等等客戶端異常中斷等等 當(dāng)若服務(wù)端宕機(jī)了,客戶端怎么做、服務(wù)端再次上線時(shí)怎么做?客戶端則需要斷開連接,通過 onclose 關(guān)閉連接,服務(wù)端再次上線時(shí)則需要清除之間存的數(shù)據(jù),若不清除 則會(huì)造成只要請(qǐng)求到服務(wù)端的都會(huì)被視為離線。

針對(duì)這種異常的中斷解決方案就是處理重連,下面我們給出的重連方案是使用js庫(kù)處理:引入reconnecting-websocket.min.js,ws建立鏈接方法使用js庫(kù)api方法:

var ws = new ReconnectingWebSocket(url); // 斷線重連: reconnectSocket(){ if ('ws' in window) { ws = new ReconnectingWebSocket(url); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(url); } else { ws = new SockJS(url); } }

斷網(wǎng)監(jiān)測(cè)支持使用js庫(kù):offline.min.js

onLineCheck(){ Offline.check(); console.log(Offline.state,'---Offline.state'); console.log(this.socketStatus,'---this.socketStatus'); if(!this.socketStatus){ console.log('網(wǎng)絡(luò)連接已斷開!'); if(Offline.state === 'up' && websocket.reconnectAttempts > websocket.maxReconnectInterval){ window.location.reload(); } reconnectSocket(); }else{ console.log('網(wǎng)絡(luò)連接成功!'); websocket.send('heartBeat'); } } // 使用:在websocket斷開鏈接時(shí)調(diào)用網(wǎng)絡(luò)中斷監(jiān)測(cè) websocket.onclose => () { onLineCheck(); };

以上方案,只是拋磚引玉,如果大家有更好的解決方案歡迎評(píng)論區(qū)分享交流。

七、總結(jié)

WebSocket 是為了在 web 應(yīng)用上進(jìn)行雙通道通信而產(chǎn)生的協(xié)議,相比于輪詢HTTP請(qǐng)求的方式,WebSocket 有節(jié)省服務(wù)器資源,效率高等優(yōu)點(diǎn)。WebSocket 中的掩碼是為了防止早期版本中存在中間緩存污染攻擊等問題而設(shè)置的,客戶端向服務(wù)端發(fā)送數(shù)據(jù)需要掩碼,服務(wù)端向客戶端發(fā)送數(shù)據(jù)不需要掩碼。WebSocket 中 Sec-WebSocket-Key 的生成算法是拼接服務(wù)端和客戶端生成的字符串,進(jìn)行SHA1哈希算法,再用base64編碼。WebSocket 協(xié)議握手是依靠 HTTP 協(xié)議的,依靠于 HTTP 響應(yīng)101進(jìn)行協(xié)議升級(jí)轉(zhuǎn)換。

參考

  • 阮一峰:WebSocket 教程
  • 看完讓你徹底理解 WebSocket 原理

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多