摘要:注意值得注意的是,一旦接收到所有幀并且原始消息有效載荷已被重建,客戶端將僅被通知關(guān)于新消息。實驗表明,在之后創(chuàng)建了第二個幀。以下值目前正在使用中代表繼續(xù)幀。
這一次,我們將深入到通信協(xié)議的世界中,對比并討論它們的屬性并構(gòu)建部件。我們將提供WebSockets和HTTP / 2的快速比較。 最后,我們分享一些關(guān)于如何選擇網(wǎng)絡(luò)協(xié)議。
概述如今,擁有豐富動態(tài)用戶界面的復(fù)雜網(wǎng)絡(luò)應(yīng)用程序被視為理所當然。這并不奇怪 - 互聯(lián)網(wǎng)自成立以來已經(jīng)走過了很長的一段路。
最初,互聯(lián)網(wǎng)并不是為支持這種動態(tài)和復(fù)雜的網(wǎng)絡(luò)應(yīng)用程序而構(gòu)建的。它被認為是HTML頁面的集合,彼此鏈接以形成包含信息的“web”概念。一切都基本上圍繞HTTP的所謂的請求/響應(yīng)范式而建立。客戶端加載一個頁面,然后什么都不會發(fā)生,直到用戶點擊并導(dǎo)航到下一頁。
大約在2005年,AJAX被引入,許多人開始探索在客戶端和服務(wù)器之間建立雙向連接的可能性。盡管如此,所有HTTP通信都是由客戶端引導(dǎo)的,這需要用戶交互或定期輪詢從服務(wù)器加載新數(shù)據(jù)。
使HTTP成為“雙向”使服務(wù)器“主動”向客戶端發(fā)送數(shù)據(jù)的技術(shù)已經(jīng)存在了相當長的一段時間。例如“Push”和“Comet”等等。
服務(wù)器向客戶端發(fā)送數(shù)據(jù)的最常見竅門之一稱為長輪詢。通過長輪詢,客戶端打開一個HTTP連接到服務(wù)器,該服務(wù)器保持打開狀態(tài)直到發(fā)送響應(yīng)。只要服務(wù)器有新的數(shù)據(jù)需要發(fā)送,它就會將其作為響應(yīng)發(fā)送出去。
我們來看看一個非常簡單的長輪詢片段的樣子:
(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); })();
這基本上是自動執(zhí)行的功能,自動運行第一次。 它設(shè)置十(10)秒的間隔,并在每次異步Ajax調(diào)用服務(wù)器之后,回調(diào)再次調(diào)用ajax。
其他技術(shù)涉及Flash或XHR多部分請求和所謂的htmlfiles。
所有這些解決方案都有相同的問題:它們承載HTTP的開銷,這并不能使它們非常適合低延遲的應(yīng)用程序。在瀏覽器或任何其他具有實時組件的在線游戲中考慮多人第一人稱射擊游戲。
WebSockets的引入WebSocket規(guī)范定義了在Web瀏覽器和服務(wù)器之間建立“套接字”連接的API。 簡而言之:客戶端和服務(wù)器之間存在持久連接,并且雙方可以隨時開始發(fā)送數(shù)據(jù)。
客戶端通過稱為WebSocket握手的過程建立WebSocket連接。該過程從客戶端向服務(wù)器發(fā)送常規(guī)HTTP請求開始。此請求中包含Upgrade頭信息,通知服務(wù)器客戶端希望建立WebSocket連接。
我們來看看如何在客戶端打開WebSocket連接:
// 創(chuàng)建一個加密連接的WebSocket var socket = new WebSocket("ws://websocket.example.com");
這個scheme只是開始打開websocket.example.com的WebSocket連接的過程。
以下是初始請求頭的簡化示例:
GET ws://websocket.example.com/ HTTP/1.1 Origin: http://example.com Connection: Upgrade Host: websocket.example.com Upgrade: websocket
如果服務(wù)器支持WebSocket協(xié)議,它將同意Upgrade,并將通過響應(yīng)中的Upgrade頭進行通信。
我們來看看如何在Node.JS中實現(xiàn)這個功能:
// We"ll be using the https://github.com/theturtle32/WebSocket-Node // WebSocket implementation var WebSocketServer = require("websocket").server; var http = require("http"); var server = http.createServer(function(request, response) { // process HTTP request. }); server.listen(1337, function() { }); // create the server wsServer = new WebSocketServer({ httpServer: server }); // WebSocket server wsServer.on("request", function(request) { var connection = request.accept(null, request.origin); // This is the most important callback for us, we"ll handle // all messages from users here. connection.on("message", function(message) { // Process WebSocket message }); connection.on("close", function(connection) { // Connection closes }); });
連接建立后,服務(wù)器通過Upgrade進行回復(fù):
HTTP/1.1 101 Switching Protocols Date: Wed, 25 Oct 2017 10:07:34 GMT Connection: Upgrade Upgrade: WebSocket
連接建立后,open事件將在客戶端的WebSocket實例上觸發(fā):
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."); };
現(xiàn)在握手已經(jīng)完成,初始HTTP連接被替換為使用相同底層TCP / IP連接的WebSocket連接。此時,任何一方都可以開始發(fā)送數(shù)據(jù)。
借助WebSocket,您可以隨心所欲地傳輸盡可能多的數(shù)據(jù),而不會產(chǎn)生與傳統(tǒng)HTTP請求相關(guān)的開銷。數(shù)據(jù)通過WebSocket作為消息傳輸,每個消息由一個或多個包含您要發(fā)送的數(shù)據(jù)(有效負載)的幀組成。 為了確保消息在到達客戶端時能夠被正確地重建,每個幀都以4-12字節(jié)的有效負載數(shù)據(jù)作為前綴。 使用這種基于幀的消息傳遞系統(tǒng)有助于減少傳輸?shù)姆怯行лd荷數(shù)據(jù)量,從而顯著減少延遲。
注意:值得注意的是,一旦接收到所有幀并且原始消息有效載荷已被重建,客戶端將僅被通知關(guān)于新消息。
WebSocket URLs我們之前簡要提到過,WebSockets引入了一個新的URL方案。實際上,他們引入了兩個新的方案:ws://和wss://。
網(wǎng)址具有特定schema語法。WebSocket URL特別之處在于它們不支持錨(#sample_anchor)。
對于HTTP風格的URL,相同的規(guī)則適用于WebSocket風格的URL。ws未加密,默認端口為80,而wss需要TLS加密并且端口443為默認端口。
成幀協(xié)議讓我們更深入地了解組幀協(xié)議。 這是RFC為我們提供的內(nèi)容:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
從RFC指定的WebSocket版本開始,每個數(shù)據(jù)包前只有一個標頭。不過,這是一個相當復(fù)雜的標題。以下是其解釋的構(gòu)建塊:
fin(1比特):指示該幀是否構(gòu)成消息的最終幀。大多數(shù)情況下,消息適合于一個幀,并且該位總是被設(shè)置。實驗表明,F(xiàn)irefox在32K之后創(chuàng)建了第二個幀。
rsv1,rsv2,rsv3(每個1位):除非擴展協(xié)商定義了非零值的含義,否則必須為0。如果接收到非零值并且沒有任何協(xié)商的擴展定義這種非零值的含義,則接收端點必須失敗連接。
opcode(4位):說明幀代表什么。以下值目前正在使用中:
0x00:代表繼續(xù)幀。
0x01:代表文本幀。
0x02:代表二進制幀。
0x08:該幀終止連接。
0x09:這個幀是一個ping。
0x0a:這個幀是一個pong。
(正如您所看到的,有足夠的值未被使用;它們已被保留供將來使用)。
掩碼(1位):指示連接是否被屏蔽。就目前而言,從客戶端到服務(wù)器的每條消息都必須被屏蔽,并且規(guī)范會在未被屏蔽的情況下終止連接。
payload_len(7比特):有效載荷的長度。 WebSocket幀包含以下長度括號:
0-125表示有效載荷的長度。 126表示以下兩個字節(jié)表示長度,127表示接下來的8個字節(jié)表示長度。所以有效負載的長度在?7位,16位和64位括號內(nèi)。
屏蔽鍵(32位):從客戶端發(fā)送到服務(wù)器的所有幀都被幀中包含的32位值屏蔽。
有效載荷:最可能被掩蓋的實際數(shù)據(jù)。它的長度是payload_len的長度。
為什么WebSocket基于框架而不是基于流?我不知道,只是和你一樣,我很想了解更多,所以如果你有一個想法,請隨時在下面的回復(fù)中添加評論和資源。另外,關(guān)于這個主題的討論可以在HackerNews上找到。
關(guān)于幀的數(shù)據(jù)如上所述,數(shù)據(jù)可以分成多個幀。傳輸數(shù)據(jù)的第一幀有一個操作碼,表示正在傳輸什么類型的數(shù)據(jù)。 這是必要的,因為在規(guī)范開始時JavaScript幾乎不存在對二進制數(shù)據(jù)的支持。0x01表示utf-8編碼的文本數(shù)據(jù),0x02是二進制數(shù)據(jù)。大多數(shù)人會傳輸JSON,在這種情況下,您可能想要選擇文本操作碼。當你發(fā)射二進制數(shù)據(jù)時,它將在瀏覽器特定的Blob中表示。
通過WebSocket發(fā)送數(shù)據(jù)的API非常簡單:
socket.onopen = function(event) { socket.send("Some message"); // Sends data to server. };
當WebSocket接收數(shù)據(jù)時(在客戶端),message事件被觸發(fā)。此事件包含一個稱為data的屬性,可用于訪問消息的內(nèi)容。
// Handle messages sent by the server. socket.onmessage = function(event) { var message = event.data; console.log(message); };
您可以使用Chrome DevTools中的Network選項卡輕松瀏覽WebSocket連接中每個幀中的數(shù)據(jù):
碎片有效載荷數(shù)據(jù)可以分成多個獨立的幀。接收端應(yīng)該緩沖它們直到fin位置位。所以你可以通過11個6(頭部長度)包+每個1字節(jié)的字符串傳輸字符串“Hello World”。控制包不允許使用碎片。但是,規(guī)范要求您能夠處理交錯控制幀。這是在TCP包以任意順序到達的情況下。
合并幀的邏輯大致如下:
接收第一幀
記得操作碼
將幀有效載荷連接在一起,直到fin位被設(shè)置
斷言每個包的操作碼都是零
分段的主要目的是在消息啟動時允許發(fā)送未知大小的消息。通過分段,服務(wù)器可以選擇合理大小的緩沖區(qū),并且當緩沖區(qū)滿時,將一個片段寫入網(wǎng)絡(luò)。分片的次要用例是多路復(fù)用,其中一個邏輯信道上的大消息接管整個輸出信道是不可取的,因此多路復(fù)用需要自由將消息分成更小的片段以更好地共享輸出渠道。
什么是心跳?在握手之后的任何時候,客戶端或服務(wù)器都可以選擇向?qū)Ψ桨l(fā)送ping命令。當收到ping時,收件人必須盡快發(fā)回pong。這是一個心跳。您可以使用它來確保客戶端仍處于連接狀態(tài)。
ping或pong只是一個常規(guī)幀,但它是一個控制幀。 ping具有0x9的操作碼,并且pongs具有0xA的操作碼。當你得到一個ping之后,發(fā)回一個與ping完全相同的Payload Data的pong(對于ping和pongs,最大有效載荷長度是125)。你也可能在沒有發(fā)送ping的情況下得到一個pong。如果發(fā)生,請忽略它。
心跳非常有用。有些服務(wù)(如負載均衡器)會終止空閑連接。另外,接收方不可能看到遠端是否已經(jīng)終止。只有在下一次發(fā)送時你才會意識到出了問題。
處理錯誤您可以通過監(jiān)聽錯誤事件來處理發(fā)生的任何錯誤。
它看起來像這樣:
var socket = new WebSocket("ws://websocket.example.com"); // Handle any error that occurs. socket.onerror = function(error) { console.log("WebSocket Error: " + error); };關(guān)閉連接
要關(guān)閉連接,客戶端或服務(wù)器應(yīng)發(fā)送包含操作碼0x8的數(shù)據(jù)的控制幀。一旦接收到這樣的幀,對方發(fā)送一個關(guān)閉幀作為響應(yīng)。第一個同伴然后關(guān)閉連接。 然后放棄關(guān)閉連接后收到的任何其他數(shù)據(jù)。
這是您如何啟動從客戶端關(guān)閉WebSocket連接的方式:
// Close if the connection is open. if (socket.readyState === WebSocket.OPEN) { socket.close(); }
另外,為了在關(guān)閉完成后執(zhí)行任何清理,您可以將事件偵聽器附加到關(guān)閉事件:
// Do necessary clean up. socket.onclose = function(event) { console.log("Disconnected from WebSocket."); };
服務(wù)器必須偵聽關(guān)閉事件以便在需要時處理它:
connection.on("close", function(reasonCode, description) { // The connection is getting closed. });WebSockets和HTTP/2對比
盡管HTTP/2具有很多功能,但并不能完全取代現(xiàn)有推送/流媒體技術(shù)的需求。
關(guān)于HTTP/2的第一件重要事情是,它不能代替所有的HTTP。 動詞,狀態(tài)代碼和大部分標題將保持與今天相同。HTTP/2是關(guān)于提高數(shù)據(jù)在線路上傳輸方式的效率。
現(xiàn)在,如果我們比較HTTP / 2和WebSocket,我們可以看到很多相似之處:
正如我們上面看到的那樣,HTTP/2引入了服務(wù)器推送,它使服務(wù)器能夠主動發(fā)送資源到客戶端緩存。但是,它并不允許將數(shù)據(jù)推送到客戶端應(yīng)用程序本身。服務(wù)器推送只能由瀏覽器處理,并且不會在應(yīng)用程序代碼中彈出,這意味著應(yīng)用程序沒有API來獲取這些事件的通知。
這是Server-Sent Events(SSE)變得非常有用的地方。SSE是一種允許服務(wù)器在建立客戶端 - 服務(wù)器連接后將數(shù)據(jù)異步推送到客戶端的機制。只要有新的“大塊”數(shù)據(jù)可用,服務(wù)器就可以決定發(fā)送數(shù)據(jù)。它可以被認為是一種單向發(fā)布 - 訂閱模式。它還提供了一個標準的JavaScript客戶端API,名為EventSource,在大多數(shù)現(xiàn)代瀏覽器中實現(xiàn),作為W3C的HTML5標準的一部分。請注意,不支持EventSource API的瀏覽器可以很容易地被polyfilled。
由于SSE基于HTTP,因此它非常適合HTTP/2,并且可以結(jié)合使用以實現(xiàn)最佳效果:HTTP/2基于多路復(fù)用流處理高效傳輸層,SSE將API提供給應(yīng)用程序以啟用推。
為了充分理解Streams和Multiplexing是什么,我們首先看看IETF的定義:“stream”是在HTTP / 2連接中在客戶端和服務(wù)器之間交換的一個獨立的雙向幀序列。其主要特征之一是單個HTTP/2連接可以包含多個同時打開的流,其中來自多個流的端點交織幀。
我們必須記住SSE是基于HTTP的。這意味著在HTTP/2中,不僅可以將多個SSE流交織到單個TCP連接上,還可以通過多個SSE流(服務(wù)器到客戶端推送)和多個客戶端請求(客戶端到服務(wù)器)。由于HTTP/2和SSE,現(xiàn)在我們有一個純粹的HTTP雙向連接和一個簡單的API,讓應(yīng)用程序代碼注冊到服務(wù)器推送。將SSE與WebSocket進行比較時,缺乏雙向功能通常被認為是一個主要缺陷。 由于HTTP/2,這不再是這種情況。這為跳過WebSocket并堅持使用基于HTTP的信號提供了機會。
WebSocket還是HTTP/2WebSockets肯定會在HTTP / 2 + SSE的控制下生存下去,主要是因為它是一種已經(jīng)被很好地采用的技術(shù),并且在非常具體的使用情況下,它比HTTP/2具有優(yōu)勢,因為它已經(jīng)以較少的開銷構(gòu)建用于雙向能力(例如頭)。
假設(shè)你想構(gòu)建一個Massive Multiplayer在線游戲,需要來自連接兩端的大量消息。在這種情況下,WebSockets的性能會好很多。
通常,只要需要客戶端和服務(wù)器之間的真正低延遲,近實時的連接,就使用WebSocket。請記住,這可能需要重新考慮如何構(gòu)建服務(wù)器端應(yīng)用程序,以及將焦點轉(zhuǎn)移到事件隊列等技術(shù)上。
如果您的使用案例需要顯示實時市場新聞,市場數(shù)據(jù),聊天應(yīng)用程序等,依靠HTTP/2 + SSE將為您提供高效的雙向溝通??渠道,同時獲得留在HTTP世界的好處:
當考慮到與現(xiàn)有Web基礎(chǔ)架構(gòu)的兼容性時,WebSocket通常會成為痛苦的源頭,因為它將HTTP連接升級到與HTTP無關(guān)的完全不同的協(xié)議。
規(guī)模和安全性:Web組件(防火墻,入侵檢測,負載平衡器)是以HTTP為基礎(chǔ)構(gòu)建,維護和配置的,這是大型/關(guān)鍵應(yīng)用程序在彈性,安全性和可伸縮性方面更喜歡的環(huán)境。
另外,您必須考慮瀏覽器支持。看看WebSocket:
實際上相當不錯,不是嗎?
然而,HTTP/2的情況并不相同:
僅TLS(不是很糟糕)
部分支持IE 11,但僅限于Windows 10
僅在Safari中支持OSX 10.11+
如果您可以通過ALPN進行協(xié)商,則僅支持HTTP/2(您的服務(wù)器需要明確支持)
SSE支持的更好:
只有IE / Edge不提供支持。(好吧,Opera Mini既不支持SSE也不支持WebSocket,所以我們可以將其完全排除在外)。 在IE / Edge中有一些體面的polyfills用于SSE支持。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/107748.html
摘要:幀協(xié)議讓我們深入了解下幀協(xié)議。目前可用的值該幀接續(xù)前面一幀的有效載荷。該幀包含二進制數(shù)據(jù)。幀有以下幾類長度表示有效載荷的長度。數(shù)據(jù)分片有效載荷數(shù)據(jù)可以被分成多個獨立的幀。接收端會緩沖這些幀直到位有值。 原文請查閱這里,略有改動,本文采用知識共享署名 3.0 中國大陸許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...
摘要:數(shù)據(jù)作為消息通過傳輸,每個消息由一個或多個幀組成,其中包含正在發(fā)送的數(shù)據(jù)有效負載。幀數(shù)據(jù)如上所述,數(shù)據(jù)可以被分割成多個幀。但是,規(guī)范希望能夠處理交錯的控制幀。 文章底部分享給大家一套 react + socket 實戰(zhàn)教程 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第5篇。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯過了前面的章...
摘要:下面我們從前端基礎(chǔ)和底層原理開始講起。對于和這三個對應(yīng)于矢量圖位圖和圖的渲染來說,給前端開發(fā)帶來了重武器,很多小游戲也因此蓬勃發(fā)展。這篇文章受眾之大,后來被人重新整理并發(fā)布為,其中還包括中文版。 showImg(https://segmentfault.com/img/bVbjM5r?w=1142&h=640); 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 這...
摘要:發(fā)布本周正式發(fā)布,包含了一系列的特性提升與問題修復(fù),同時也在不斷致力于將打造地更為輕巧與高性能。當然,姜振勇老師還會介紹的多種服務(wù),包括大數(shù)據(jù)網(wǎng)絡(luò)和安全,展現(xiàn)彈性安全和高可擴展性的全方位能力。 showImg(http://upload-images.jianshu.io/upload_images/1647496-2ce7598e6987d9af.jpg?imageMogr2/aut...
閱讀 2722·2021-11-11 17:21
閱讀 613·2021-09-23 11:22
閱讀 3578·2019-08-30 15:55
閱讀 1641·2019-08-29 17:15
閱讀 573·2019-08-29 16:38
閱讀 904·2019-08-26 11:54
閱讀 2504·2019-08-26 11:53
閱讀 2749·2019-08-26 10:31