您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關JavaScript探索websocket和HTTP/2與SSE的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
簡介
如今,功能豐富、動態 ui 的復雜 web 應用程序被認為是理所當然。這并不奇怪——互聯網自誕生以來已經走過了漫長的道路。
最初,互聯網并不是為了支持這種動態和復雜的 web 應用程序而構建的。它被認為是HTML頁面的集合,相互鏈接形成一個包含信息的 “web” 概念。一切都是圍繞 HTTP 的所謂 請求/響應 范式構建的。客戶端加載一個頁面,然后在用戶單擊并導航到下一個頁面之前什么都不會發生。
大約在2005年,AJAX被引入,很多人開始探索在客戶端和服務器之間建立雙向連接的可能性。盡管如此,所有HTTP 通信都由客戶端引導,客戶端需要用戶交互或定期輪詢以從服務器加載新數據。
讓 HTTP 變成“雙向”交互
讓服務器能夠“主動”向客戶機發送數據的技術已經出現了相當長的時間。例如“Push”和“Comet”。
最常見的一種黑客攻擊方法是讓服務器產生一種需要向客戶端發送數據的錯覺,這稱為長輪詢。通過長輪詢,客戶端打開與服務器的 HTTP 連接,使其保持打開狀態,直到發送響應為止。 每當服務器有新數據時需要發送時,就會作為響應發送。
看看一個非常簡單的長輪詢代碼片段是什么樣的:
(function poll(){ setTimeout(function(){ $.ajax({ url: 'https://api.example.com/endpoint', success: function(data) { // Do something with `data` // ... //Setup the next poll recursively poll(); }, dataType: 'json' }); }, 10000); })();
這基本上是一個自執行函數,第一次立即運行時,它設置了 10 秒間隔,在對服務器的每個異步Ajax調用之后,回調將再次調用Ajax。
其他技術涉及 Flash 或 XHR multipart request 和所謂的 htmlfiles 。
但是,所有這些工作區都有一個相同的問題:它們都帶有 HTTP 的開銷,這使得它們不適合于低延遲應用程序。想想瀏覽器中的多人第一人稱射擊游戲或任何其他帶有實時組件的在線游戲。
WebSockets 的引入
WebSocket 規范定義了在 web 瀏覽器和服務器之間建立“套接字”連接的 API。簡單地說:客戶機和服務器之間存在長久連接,雙方可以隨時開始發送數據。
客戶端通過 WebSocket 握手 過程建立 WebSocket 連接。這個過程從客戶機向服務器發送一個常規 HTTP 請求開始,這個請求中包含一個升級頭,它通知服務器客戶機希望建立一個 WebSocket 連接。
客戶端建立 WebSocket 連接方式如下:
// Create a new WebSocket with an encrypted connection. var socket = new WebSocket('ws://websocket.example.com')
WebSocket url使用 ws 方案。還有 wss 用于安全的 WebSocket 連接,相當于HTTPS。
這個方案只是打開 websocket.example.com 的 WebSocket 連接的開始。
下面是初始請求頭的一個簡化示例:
如果服務器支持 WebSocke t協議,它將同意升級,并通過響應中的升級頭進行通信。
Node.js 的實現方式:
建立連接后,服務器通過升級頭部中內容時行響應:
一旦建立連接,open 事件將在客戶端 WebSocket 實例上被觸發:
var socket = new WebSocket('ws://websocket.example.com'); // Show a connected message when the WebSocket is opened. socket.onopen = function(event) { console.log('WebSocket is connected.'); };
現在握手已經完成,初始 HTTP 連接被使用相同底層 TCP/IP 連接的 WebSocket 連接替換。此時,雙方都可以開始發送數據。
使用 WebSockets,可以傳輸任意數量的數據,而不會產生與傳統 HTTP 請求相關的開銷。數據作為消息通過 WebSocket 傳輸,每個消息由一個或多個幀組成,其中包含正在發送的數據(有效負載)。為了確保消息在到達客戶端時能夠正確地進行重構,每一幀都以負載的4-12字節數據為前綴, 使用這種基于幀的消息傳遞系統有助于減少傳輸的非有效負載數據量,從而大大的減少延遲。
注意:值得注意的是,只有在接收到所有幀并重構了原始消息負載之后,客戶機才會收到關于新消息的通知。
WebSocket URLs
之前簡要提到過 WebSockets 引入了一個新的URL方案。實際上,他們引入了兩個新的方案:ws:// 和wss://。
url 具有特定方案的語法。WebSocket url 的特殊之處在于它們不支持錨點(#sample_anchor)。
同樣的規則適用于 WebSocket 風格的url和 HTTP 風格的 url。ws 是未加密的,默認端口為80,而 wss 需要TLS加密,默認端口為 443。
幀協議
更深入地了解幀協議,這是 RFC 為我們提供的:
在RFC 指定的 WebSocket 版本中,每個包前面只有一個報頭。然而,這是一個相當復雜的報頭。以下是它的構建模塊:
FIN
:1bit ,表示是消息的最后一幀,如果消息只有一幀那么第一幀也就是最后一幀,Firefox 在 32K 之后創建了第二個幀。
RSV1,RSV2,RSV3
:每個1bit,必須是0,除非擴展定義為非零。如果接受到的是非零值但是擴展沒有定義,則需要關閉連接。
Opcode
:4bit,解釋 Payload 數據,規定有以下不同的狀態,如果是未知的,接收方必須馬上關閉連接。狀態如下:
0x00: 附加數據幀
0x01:文本數據幀
0x02:二進制數據幀
0x3-7:保留為之后非控制幀使用
0x8:關閉連接幀
0x9:ping
0xA:pong
0xB-F(保留為后面的控制幀使用)
Mask
:1bit,掩碼,定義payload數據是否進行了掩碼處理,如果是1表示進行了掩碼處理。
Masking-key
:域的數據即是掩碼密鑰,用于解碼PayloadData。客戶端發出的數據幀需要進行掩碼處理,所以此位是1。
Payload_len
:7位,7 + 16位,7+64位,payload數據的長度,如果是0-125,就是真實的payload長度,如果是126,那么接著后面的2個字節對應的16位無符號整數就是payload數據長度;如果是127,那么接著后面的8個字節對應的64位無符號整數就是payload數據的長度。
Masking-key
:0到4字節,如果MASK位設為1則有4個字節的掩碼解密密鑰,否則就沒有。
Payload data
:任意長度數據。包含有擴展定義數據和應用數據,如果沒有定義擴展則沒有此項,僅含有應用數據。
為什么 WebSocket 是基于幀而不是基于流?我不知道,就像你一樣,我很想了解更多,所以如果你有想法,請隨時在下面的回復中添加評論和資源。另外,關于這個主題的討論可以在 HackerNews 上找到。
幀數據
如上所述,數據可以被分割成多個幀。 傳輸數據的第一幀有一個操作碼,表示正在傳輸什么類型的數據。 這是必要的,因為 JavaScript 在開始規范時幾乎不存在對二進制數據的支持。 0x01 表示 utf-8 編碼的文本數據,0x02 是二進制數據。大多數人會發送 JSON ,在這種情況下,你可能要選擇文本操作碼。 當你發送二進制數據時,它將在瀏覽器特定的 Blob 中表示。
通過 WebSocket 發送數據的API非常簡單:
var socket = new WebSocket('ws://websocket.example.com'); socket.onopen = function(event) { socket.send('Some message'); // Sends data to server. };
當 WebSocket 接收數據時(在客戶端),會觸發一個消息事件。此事件包括一個名為data的屬性,可用于訪問消息的內容。
// Handle messages sent by the server. socket.onmessage = function(event) { var message = event.data; console.log(message); };
在Chrome開發工具:可以很容易地觀察 WebSocket 連接中每個幀中的數據:
消息分片
有效載荷數據可以分成多個單獨的幀。接收端應該對它們進行緩沖,直到設置好 fin
位。因此,可以將字符串“Hello World”發送到11個包中,每個包的長度為6(報頭長度)+ 1字節。控件包不允許分片。但是,規范希望能夠處理交錯的控制幀。這是TCP包以任意順序到達的情況。
連接幀的邏輯大致如下:
接收第一幀
記住操作碼
將幀有效負載連接在一起,直到 fin 位被設置
斷言每個包的操作碼是零
分片目的是發送長度未知的消息。如果不分片發送,即一幀,就需要緩存整個消息,計算其長度,構建frame并發送;使用分片的話,可使用一個大小合適的buffer,用消息內容填充buffer,填滿即發送出去。
什么是跳動檢測?
主要目的是保障客戶端 websocket 與服務端連接狀態,該程序有心跳檢測及自動重連機制,當網絡斷開或者后端服務問題造成客戶端websocket斷開,程序會自動嘗試重新連接直到再次連接成功。
在使用原生websocket的時候,如果設備網絡斷開,不會觸發任何函數,前端程序無法得知當前連接已經斷開。這個時候如果調用 websocket.send 方法,瀏覽器就會發現消息發不出去,便會立刻或者一定短時間后(不同瀏覽器或者瀏覽器版本可能表現不同)觸發 onclose 函數。
后端 websocket 服務也可能出現異常,連接斷開后前端也并沒有收到通知,因此需要前端定時發送心跳消息 ping,后端收到 ping 類型的消息,立馬返回 pong 消息,告知前端連接正常。如果一定時間沒收到pong消息,就說明連接不正常,前端便會執行重連。
為了解決以上兩個問題,以前端作為主動方,定時發送 ping 消息,用于檢測網絡和前后端連接問題。一旦發現異常,前端持續執行重連邏輯,直到重連成功。
錯誤處理
以通過監聽 error 事件來處理所有錯誤:
var socket = new WebSocket('ws://websocket.example.com'); // Handle any error that occurs. socket.onerror = function(error) { console.log('WebSocket Error: ' + error); };
關閉連接
要關閉連接,客戶機或服務器都應該發送包含操作碼0x8
的數據的控制幀。當接收到這樣一個幀時,另一個對等點發送一個關閉幀作為響應,然后第一個對等點關閉連接,關閉連接后接收到的任何其他數據都將被丟棄:
// Close if the connection is open. if (socket.readyState === WebSocket.OPEN) { socket.close(); }
另外,為了在完成關閉之后執行其他清理,可以將事件偵聽器附加到關閉事件:
// Do necessary clean up. socket.onclose = function(event) { console.log('Disconnected from WebSocket.'); };
服務器必須監聽關閉事件以便在需要時處理它:
connection.on('close', function(reasonCode, description) { // The connection is getting closed. });
WebSockets和HTTP/2 比較
雖然HTTP/2提供了很多功能,但它并沒有完全滿足對現有推送/流技術的需求。
關于 HTTP/2 的第一個重要的事情是它并不能替代所有的 HTTP 。verb、狀態碼和大部分頭信息將保持與目前版本一致。HTTP/2 是意在提升數據在線路上傳輸的效率。
比較HTTP/2和WebSocket,可以看到很多相似之處:
正如我們在上面看到的,HTTP/2引入了 Server Push,它使服務器能夠主動地將資源發送到客戶機緩存。但是,它不允許將數據下推到客戶機應用程序本身,服務器推送只由瀏覽器處理,不會在應用程序代碼中彈出,這意味著應用程序沒有API來獲取這些事件的通知。
這就是服務器發送事件(SSE)變得非常有用的地方。SSE 是一種機制,它允許服務器在建立客戶機-服務器連接之后異步地將數據推送到客戶機。然后,只要有新的“數據塊”可用,服務器就可以決定發送數據。它可以看作是單向發布-訂閱模式。它還提供了一個名為 EventSource API 的標準JavaScript,作為W3C HTML5標準的一部分,在大多數現代瀏覽器中實現。不支持 EventSource API 的瀏覽器可以輕松地使用 polyfilled 方案來解決。
由于 SSE 基于 HTTP ,因此它與 HTTP/2 非常合適,可以結合使用以實現最佳效果:HTTP/2 處理基于多路復用流的高效傳輸層,SSE 將 API 提供給應用以啟用數據推送。
為了理解 Streams 和 Multiplexing 是什么,首先看一下`IETF
定義:“stream”是在HTTP/2 連接中客戶機和服務器之間交換的獨立的、雙向的幀序列。它的一個主要特征是,一個HTTP/2 連接可以包含多個并發打開的流,任何一個端點都可以從多個流中交錯幀。
SSE 是基于 HTTP 的,這說明在 HTTP/2 中,不僅可以將多個 SSE 流交織到單個 TCP 連接上,而且還可以通過多個 SSE 流(服務器到客戶端的推送)和多個客戶端請求(客戶端到服務器)。因為有 HTTP/2 和 SSE 的存在,現在有一個純粹的 HTTP 雙向連接和一個簡單的 API 就可以讓應用程序代碼注冊到服務器推送服務上。在比較 SSE 和 WebSocket 時,缺乏雙向能力往往被認為是一個主要的缺陷。有了 HTTP/2,不再有這種情況。這樣就可以跳過 WebSocket ,而堅持使用基于 HTTP 的信號機制。
如何選擇WebSocket和HTTP/2?
WebSockets 會在 HTTP/2 + SSE 的領域中生存下來,主要是因為它是一種已經被很好地應用的技術,并且在非常具體的使用情況下,它比 HTTP/2 更具優勢,因為它已經被構建用于具有較少開銷(如報頭)的雙向功能。
假設建立一個大型多人在線游戲,需要來自連接兩端的大量消息。在這種情況下,WebSockets 的性能會好很多。
一般情況下,只要需要客戶端和服務器之間的真正低延遲,接近實時的連接,就使用 WebSocket ,這可能需要重新考慮如何構建服務器端應用程序,以及將焦點轉移到隊列事件等技術上。
使用的方案需要顯示實時的市場消息,市場數據,聊天應用程序等,依靠 HTTP/2 + SSE 將為你提供高效的雙向通信渠道,同時獲得留在 HTTP 領域的各種好處:
當考慮到與現有 Web 基礎設施的兼容性時,WebSocket 通常會變成一個痛苦的源頭,因為它將 HTTP 連接升級到完全不同于 HTTP 的協議。
規模和安全性:Web 組件(防火墻,入侵檢測,負載均衡)是以 HTTP 為基礎構建,維護和配置的,這是大型/關鍵應用程序在彈性,安全性和可伸縮性方面更偏向的環境。
感謝各位的閱讀!關于JavaScript探索websocket和HTTP/2與SSE的示例分析就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。