摘要:幀協(xié)議讓我們深入了解下幀協(xié)議。目前可用的值該幀接續(xù)前面一幀的有效載荷。該幀包含二進(jìn)制數(shù)據(jù)。幀有以下幾類長(zhǎng)度表示有效載荷的長(zhǎng)度。數(shù)據(jù)分片有效載荷數(shù)據(jù)可以被分成多個(gè)獨(dú)立的幀。接收端會(huì)緩沖這些幀直到位有值。
原文請(qǐng)查閱這里,略有改動(dòng),本文采用知識(shí)共享署名 3.0 中國(guó)大陸許可協(xié)議共享,BY Troland。
本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。
這是 JavaScript 工作原理的第五章。
現(xiàn)在,我們將會(huì)深入通信協(xié)議的世界,繪制并討論它們的特點(diǎn)和內(nèi)部構(gòu)造。我們將會(huì)給出一份 WebSockets 和 HTTP/2 的快速比較 。在文末,我們將會(huì)分享如何正確地選擇網(wǎng)絡(luò)協(xié)議的一些見解。
簡(jiǎn)介現(xiàn)在,復(fù)雜的網(wǎng)頁(yè)程序擁有豐富的功能,這得多虧網(wǎng)頁(yè)的動(dòng)態(tài)交互能力。而這并不令人感到驚訝-因?yàn)樽曰ヂ?lián)網(wǎng)誕生,它經(jīng)歷了一段相當(dāng)長(zhǎng)的時(shí)間。
起初,互聯(lián)網(wǎng)并不是用來(lái)支持如此動(dòng)態(tài)和復(fù)雜的網(wǎng)頁(yè)程序的。它本來(lái)設(shè)想是由大量的 HTML 頁(yè)面組成的,每個(gè)頁(yè)面鏈接到其它的頁(yè)面,這樣就形成了包含信息的網(wǎng)頁(yè)的概念。一切都是極大地圍繞著所謂的 HTTP 請(qǐng)求/響應(yīng)模式來(lái)建立的。客戶端加載一個(gè)網(wǎng)頁(yè),直到用戶點(diǎn)擊頁(yè)面并導(dǎo)航到下一個(gè)網(wǎng)頁(yè)。
大約在 2005 年,引入了 AJAX,然后很多人開始探索客戶端和服務(wù)端雙向通信的可能性。然而,所有的 HTTP 鏈接是由客戶端控制的,意即必須由用戶進(jìn)行操作或者定期輪詢以從服務(wù)器加載數(shù)據(jù)。
讓 HTTP 支持雙向通信支持服務(wù)器主動(dòng)向客戶端推送數(shù)據(jù)的技術(shù)已經(jīng)出現(xiàn)了好一段時(shí)間了。比如 "Push" 和 "Comet" 技術(shù)。
長(zhǎng)輪詢是服務(wù)端主動(dòng)向客戶端發(fā)送數(shù)據(jù)的最常見的 hack 之一。通過(guò)長(zhǎng)輪詢,客戶端打開了一個(gè)到服務(wù)端的 HTTP 連接直到返回響應(yīng)數(shù)據(jù)。當(dāng)服務(wù)端有新數(shù)據(jù)需要發(fā)送時(shí),它會(huì)把新數(shù)據(jù)作為響應(yīng)發(fā)送給客戶端。
讓我們看一下簡(jiǎn)單的長(zhǎng)輪詢代碼片段:
(function poll(){ setTimeout(function(){ $.ajax({ url: "https://api.example.com/endpoint", success: function(data) { // 處理 `data` // ... //遞歸調(diào)用下一個(gè)輪詢 poll(); }, dataType: "json" }); }, 10000); })();
這基本上是一個(gè)自執(zhí)行函數(shù),第一次會(huì)自動(dòng)運(yùn)行。它每隔 10 秒鐘異步請(qǐng)求服務(wù)器并且當(dāng)每次發(fā)起對(duì)服務(wù)器的異步請(qǐng)求之后,會(huì)在回調(diào)函數(shù)里面再次調(diào)用 ajax 函數(shù)。
其它技術(shù)涉及到 Flash 和 XHR 多方請(qǐng)求以及所謂的 htmlfiles。
所有這些方案都有一個(gè)共同的問題:都帶有 HTTP 開銷,這樣就會(huì)使得它們無(wú)法滿足要求低延遲的程序。試想一下瀏覽器中的第一人稱射擊游戲或者其它要求實(shí)時(shí)組件功能的在線游戲。
WebSockets 的出現(xiàn)WebSocket 規(guī)范定義了一個(gè) API 用以在網(wǎng)頁(yè)瀏覽器和服務(wù)器建立一個(gè) "socket" 連接。通俗地講:在客戶端和服務(wù)器保有一個(gè)持久的連接,兩邊可以在任意時(shí)間開始發(fā)送數(shù)據(jù)。
客戶端通過(guò) WebSocket 握手的過(guò)程來(lái)創(chuàng)建 WebSocket 連接。在這一過(guò)程中,首先客戶端向服務(wù)器發(fā)起一個(gè)常規(guī)的 HTTP 請(qǐng)求。請(qǐng)求中會(huì)包含一個(gè) Upgrade 的請(qǐng)求頭,通知服務(wù)器客戶端想要建立一個(gè) WebSocket 連接。
讓我們看下如何在客戶端創(chuàng)建 WebSocket 連接:
// 創(chuàng)建新的加密 WebSocket 連接 var socket = new WebSocket("ws://websocket.example.com");
WebSocket 地址使用了 ws 方案。wss 是一個(gè)等同于 HTTPS 的安全的 WebSocket 連接。
該方案是打開到 websocket.example.com 的 WebSocket 連接的開始。
下面是初始化請(qǐng)求頭的簡(jiǎn)化例子。
GET ws://websocket.example.com/ HTTP/1.1 Origin: http://example.com Connection: Upgrade Host: websocket.example.com Upgrade: websocket
如果服務(wù)器支持 WebSocket 協(xié)議,它將會(huì)同意升級(jí)請(qǐng)求,然后通過(guò)在響應(yīng)里面返回 Upgrade 頭來(lái)進(jìn)行通信。
讓我們看下 Node.js 的實(shí)現(xiàn):
// 我們將會(huì)使用 https://github.com/theturtle32/WebSocket-Node 來(lái)實(shí)現(xiàn) WebSocket var WebSocketServer = require("websocket").server; var http = require("http"); var server = http.createServer(function(request, response) { // 處理 HTTP 請(qǐng)求 }); server.listen(1337, function() { }); // 創(chuàng)建服務(wù)器 wsServer = new WebSocketServer({ httpServer: server }); // WebSocket 服務(wù)器 wsServer.on("request", function(request) { var connection = request.accept(null, request.origin); // 這是最重要的回調(diào),在這里處理所有用戶返回的信息 connection.on("message", function(message) { // 處理 WebSocket 信息 }); connection.on("close", function(connection) { // 關(guān)閉連接 }); });
連接建立之后,服務(wù)器使用升級(jí)來(lái)作為回復(fù):
HTTP/1.1 101 Switching Protocols Date: Wed, 25 Oct 2017 10:07:34 GMT Connection: Upgrade Upgrade: WebSocket
一旦連接建立,會(huì)觸發(fā)客戶端 WebSocket 實(shí)例的 open 事件。
var socket = new WebSocket("ws://websocket.example.com"); // WebSocket 連接打開的時(shí)候,打印出 WebSocket 已連接的信息 socket.onopen = function(event) { console.log("WebSocket is connected."); };
現(xiàn)在,握手結(jié)束了,最初的 HTTP 連接被替換為 WebSocket 連接,該連接底層使用同樣的 TCP/IP 連接?,F(xiàn)在兩邊都可以開始發(fā)送數(shù)據(jù)了。
通過(guò) WebSocket,你可以隨意發(fā)送數(shù)據(jù)而不用擔(dān)心傳統(tǒng) HTTP 請(qǐng)求所帶來(lái)的相關(guān)開銷。數(shù)據(jù)是以消息的形式通過(guò) WebSocket 進(jìn)行傳輸?shù)模織l信息是由包含你所傳輸?shù)臄?shù)據(jù)(有效載荷)的一個(gè)或多個(gè)幀所組成的。為了保證當(dāng)消息到達(dá)客戶端的時(shí)候被正確地重新組裝出來(lái),每一幀都會(huì)前置關(guān)于有效載荷的 4-12 字節(jié)的數(shù)據(jù)。使用這種基于幀的信息系統(tǒng)可以幫助減少非有效載荷數(shù)據(jù)的傳輸,從而顯著地減少信息延遲。
注意:這里需要注意的是只有當(dāng)所有的消息幀都被接收到而且原始的信息有效載荷被重新組裝的時(shí)候,客戶端才會(huì)接收到新消息的通知。
WebSocket 地址前面我們簡(jiǎn)要地談到 WebSockets 引進(jìn)了一個(gè)新的地址協(xié)議。實(shí)際上,WebSocket 引進(jìn)了兩種新協(xié)議:ws:// 和 wss://。
URL 地址含有指定方案的語(yǔ)法。WebSocket 地址特別之處在于,它不支持錨(sample_anchor)。
WebSocket 和 HTTP 風(fēng)格的地址使用相同的地址規(guī)則。ws 是未加密且默認(rèn)是 80 端口,而 wss 要求 TSL 加密且默認(rèn) 443 端口。
幀協(xié)議讓我們深入了解下幀協(xié)議。這是 RFC 提供的:
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 ... | +---------------------------------------------------------------+
由于 WebSocket 版本是由 RFC 所規(guī)定的,所以每個(gè)包前面只有一個(gè)頭部信息。然而,這個(gè)頭部信息相當(dāng)?shù)膹?fù)雜。這是其組成模塊的說(shuō)明:
fin(1 位):指示是否是組成信息的最后一幀。大多數(shù)時(shí)候,信息只有一幀所以該位通常有值。測(cè)試表明火狐的第二幀數(shù)據(jù)在 32K 之后。
rsv1,rsv2,rsv3(每個(gè)一位):必須是 0 除非使用協(xié)商擴(kuò)展來(lái)定義非 0 值的含義。如果收到一個(gè)非 0 值且沒有協(xié)商擴(kuò)展來(lái)定義非零值的含義,接收端會(huì)中斷連接。
opcode(4 位):表示第幾幀。目前可用的值:
0x00:該幀接續(xù)前面一幀的有效載荷。
0x01:該幀包含文本數(shù)據(jù)。
0x02:該幀包含二進(jìn)制數(shù)據(jù)。
0x08:該幀中斷連接。
0x09:該幀是一個(gè) ping。
0x0a:該幀是一個(gè)pong。
(正如你所看到的,有相當(dāng)一部分值未被使用;它們是保留以備未來(lái)使用的)。
mask(1 位):指示該連接是否被遮罩。正其所表示的意義,每一條從客戶端發(fā)往服務(wù)器的信息都必須被遮罩,然后如果信息未遮罩,根據(jù)規(guī)范會(huì)中斷該連接。
payload_len(7 位):有效載荷的長(zhǎng)度。WebSocket 幀有以下幾類長(zhǎng)度:
0-125 表示有效載荷的長(zhǎng)度。126 意味著接下來(lái)兩個(gè)字節(jié)表示有效載荷長(zhǎng)度,127 意味著接下來(lái)的 8 個(gè)字節(jié)表示有效載荷長(zhǎng)度。所以有效載荷的長(zhǎng)度大概有 7 位,16 位和 64 位這三類。
masking-key (32 位):所有從客戶端發(fā)往服務(wù)器的幀都由幀內(nèi)的一個(gè) 32 位值所遮罩。
payload:一般情況下都會(huì)被遮罩的實(shí)際數(shù)據(jù)。其長(zhǎng)度取決于 payload_len 的長(zhǎng)度。
為什么 WebSocket 是基于幀而不是基于流的呢?我和你一樣一臉懵逼,我也想多學(xué)點(diǎn),如果你有任何想法,歡迎在下面的評(píng)論區(qū)添加評(píng)論和資源。另外,HackerNews 上面有關(guān)于這方面的討論。
幀數(shù)據(jù)正如之前提到的,數(shù)據(jù)可以被拆分為多個(gè)幀。第一幀所傳輸?shù)臄?shù)據(jù)里面含有一個(gè)操作碼表示數(shù)據(jù)的傳輸順序。這是必須的,因?yàn)楫?dāng)規(guī)范完成的時(shí)候,JavaScript 并不能很好地支持二進(jìn)制數(shù)據(jù)的傳輸。0x01 表示 utf-8 編碼的文本數(shù)據(jù),0x02 表示二進(jìn)制數(shù)據(jù)。大多數(shù)人在傳輸 JSON 數(shù)據(jù)的時(shí)候都會(huì)選擇文本操作碼。當(dāng)你傳輸二進(jìn)制數(shù)據(jù)的時(shí)候,它會(huì)以瀏覽器指定的 Blob 來(lái)表示。
通過(guò) WebSocket 來(lái)傳輸數(shù)據(jù)的 API 是非常簡(jiǎn)單的:
var socket = new WebSocket("ws://websocket.example.com"); socket.onopen = function(event) { socket.send("Some message"); // 向服務(wù)器發(fā)送數(shù)據(jù) };
當(dāng) WebSocket 正在接收數(shù)據(jù)的時(shí)候(客戶端),會(huì)觸發(fā) message 事件。該事件會(huì)帶有一個(gè) data 屬性,里面包含了消息的內(nèi)容。
// 處理服務(wù)器返回的消息 socket.onmessage = function(event) { var message = event.data; console.log(message); };
你可以很容易地利用 Chrome 開發(fā)者工具的網(wǎng)絡(luò)選項(xiàng)卡來(lái)檢查 WebSocket?連接中的每一幀的數(shù)據(jù)。
數(shù)據(jù)分片有效載荷數(shù)據(jù)可以被分成多個(gè)獨(dú)立的幀。接收端會(huì)緩沖這些幀直到 fin 位有值。所以你可以把字符串『Hello World』拆分為 11 個(gè)包,每個(gè)包由 6(頭長(zhǎng)度) + 1 字節(jié)組成。數(shù)據(jù)分片不能用來(lái)控制包。然而,規(guī)范想要你有能力去處理交錯(cuò)控制幀。這是為了預(yù)防 TCP 包無(wú)序到達(dá)客戶端。
連接幀的大概邏輯如下:
接收第一幀
記住操作碼
連接幀有效載荷直到 fin 位有值
斷言每個(gè)包的操作碼都為 0
數(shù)據(jù)分片的主要目的在于允許開始時(shí)傳輸不明大小的信息。通過(guò)數(shù)據(jù)分片,服務(wù)器可能需要設(shè)置一個(gè)合理的緩沖區(qū)大小,然后當(dāng)緩沖區(qū)滿,返回一個(gè)數(shù)據(jù)分片。數(shù)據(jù)分片的第二個(gè)用途即多路復(fù)用,邏輯通道上的大量數(shù)據(jù)占據(jù)整個(gè)輸出通道是不合理的,所以利用多路復(fù)用技術(shù)把信息拆分成更小的數(shù)據(jù)分片以更好地共享輸出通道。
心跳包握手之后的任意時(shí)刻,客戶端和服務(wù)器可以隨意地 ping 對(duì)方。當(dāng)接收到 ping 的時(shí)候,接收方必須盡快回復(fù)一個(gè) pong。此即心跳包。你可以用它來(lái)確保客戶端是否保持連接。
ping 或者 pong 雖然只是一個(gè)普通幀,但卻是一個(gè)控制幀。Ping 包含 0x9 操作碼,而 Pong 包含 0xA 操作碼。當(dāng)你接收到 ping 的時(shí)候,返回一個(gè)和 ping 攜帶同樣有效載荷數(shù)據(jù)的 pong(ping 和 pong 最大有效載荷長(zhǎng)度都為 125)。你可能接收到一個(gè) pong 而不用發(fā)送一個(gè) ping。忽略它如果有發(fā)生這樣的情況。
心跳包非常有用。利用服務(wù)(比如負(fù)載均衡器)來(lái)中斷空閑的連接。另外,接收端不可能知道服務(wù)端是否已經(jīng)中斷連接。只有在發(fā)送下一幀的時(shí)候,你才會(huì)意識(shí)到發(fā)生了錯(cuò)誤。
錯(cuò)誤處理你可以通過(guò)監(jiān)聽 error 事件來(lái)處理錯(cuò)誤。
像這樣:
var socket = new WebSocket("ws://websocket.example.com"); // 處理錯(cuò)誤 socket.onerror = function(error) { console.log("WebSocket Error: " + error); };關(guān)閉連接
客戶端或服務(wù)器可以發(fā)送一個(gè)包含 0x8 操作碼數(shù)據(jù)的控制幀來(lái)關(guān)閉連接。當(dāng)接收到控制幀的時(shí)候,另一個(gè)節(jié)點(diǎn)會(huì)返回一個(gè)關(guān)閉幀。之后第一個(gè)節(jié)點(diǎn)會(huì)關(guān)閉連接。關(guān)閉連接之后,之后接收的任何數(shù)據(jù)都會(huì)被遺棄。
這是初始化關(guān)閉客戶端的 WebSocket 連接的代碼:
// 如果連接打開著則關(guān)閉 if (socket.readyState === WebSocket.OPEN) { socket.close(); }
同樣地,為了在完成關(guān)閉連接后運(yùn)行任意的清理工作,你可以為 close 事件添加事件監(jiān)聽函數(shù):
// 運(yùn)行必要的清理工作 socket.onclose = function(event) { console.log("Disconnected from WebSocket."); };
服務(wù)器不得不監(jiān)聽 close 事件以便在需要的時(shí)候處理:
connection.on("close", function(reasonCode, description) { // 關(guān)閉連接 });WebSockets 和 HTTP/2 對(duì)比
雖然 HTTP/2 提供了很多的功能,但是它并不能完全取代當(dāng)前的 push/streaming 技術(shù)。
關(guān)于 HTTP/2 需要注意的最重要的事即它并不能完全取代 HTTP。詞匯,狀態(tài)碼以及大部分的頭部信息都會(huì)保持和現(xiàn)在一樣。HTTP/2 只是提升了線路上的數(shù)據(jù)傳輸效率。
現(xiàn)在,如果我們對(duì)比 WebSocket 和 HTTP/2,將會(huì)發(fā)現(xiàn)很多類似的地方:
正如以上所顯示的那樣,HTTP/2 引進(jìn)了 Server Push 技術(shù)用來(lái)讓服務(wù)器主動(dòng)向客戶端緩存發(fā)送數(shù)據(jù)。然而,它并不允許直接向客戶端程序本身發(fā)送數(shù)據(jù)。服務(wù)端推送只能由瀏覽器處理而不能夠在程序代碼中進(jìn)行處理,意即程序代碼沒有 API 可以用來(lái)獲取這些事件的通知。
這時(shí)候服務(wù)端推送事件(SSE)就派上用場(chǎng)了。SSE 是這樣的機(jī)制一旦客戶端-服務(wù)器連接建立,它允許服務(wù)器異步推送數(shù)據(jù)給客戶端。之后,每當(dāng)服務(wù)器產(chǎn)生新數(shù)據(jù)的時(shí)候,就推送數(shù)據(jù)給客戶端。這可以看成是單向的發(fā)布-訂閱模型。它也提供了一個(gè)被稱為 EventSource 的 標(biāo)準(zhǔn) JavaScript 客戶端 API,該 API 作為 W3C 組織發(fā)布的 HTML5 標(biāo)準(zhǔn)的一部分已經(jīng)在大多數(shù)的現(xiàn)代瀏覽器中實(shí)現(xiàn)。請(qǐng)注意不支持原生 EventSource API 的瀏覽器可以通過(guò)墊片實(shí)現(xiàn)。
由于 SSE 是基于 HTTP 的,所以它天然兼容于 HTTP/2 并且可以混合使用以利用各自的優(yōu)勢(shì): HTTP/2 處理一個(gè)基于多路復(fù)用流的高效傳輸層而 SSE 為程序提供了 API 用來(lái)支持服務(wù)端推送。
為了完全理解流和多路復(fù)用技術(shù),先讓我們來(lái)了解一下 IETF 的定義:『流』即是在一個(gè) HTTP/2 連接中,在客戶端和服務(wù)端間進(jìn)行交換傳輸?shù)囊粋€(gè)獨(dú)立的雙向幀序列。它的主要特點(diǎn)之一即單個(gè)的 HTTP/2 連接可以包含多個(gè)并發(fā)打開的流,在每一終端交錯(cuò)傳輸來(lái)自多個(gè)流的幀。
必須記住的是 SSE 是基于 HTTP 的。這意味著,通過(guò)使用 HTTP/2,不僅僅可以把多個(gè) SSE 流交叉合并成單一的 TCP 連接,還可以把多個(gè) SSE 流(服務(wù)端向客戶端推送)和多個(gè)客戶端請(qǐng)求(客戶端到服務(wù)端)合并成單一的 TCP 連接。多虧了 HTTP/2 和 SSE,現(xiàn)在我們有了一個(gè)純粹的 HTTP 雙向連接,該連接帶有一個(gè)簡(jiǎn)單的 API 允許程序代碼注冊(cè)監(jiān)聽服務(wù)端的數(shù)據(jù)推送。缺乏雙向通信能力一直被認(rèn)為是 SSE 對(duì)比 WebSocket 的主要缺點(diǎn)。多虧了 HTTP/2,這不再是缺點(diǎn)。這就讓你有機(jī)會(huì)堅(jiān)持使用基于 HTTP 的通信系統(tǒng)而非 WebSockets。
WebSocket 和 HTTP/2 的使用場(chǎng)景WebSockets 依然可以在 HTTP/2 + SSE 的統(tǒng)治下存在,主要是由于它是廣受好評(píng)的技術(shù),在特殊情況下,和 HTTP/2 比較它有一個(gè)優(yōu)點(diǎn)即它天生擁有更少的開銷(比如,頭部信息)的雙向通信能力。
假設(shè)你想要構(gòu)建一個(gè)大型的多人在線游戲,在各個(gè)連接終端會(huì)產(chǎn)生大量的信息。在這樣的情況下,WebSockets 會(huì)表現(xiàn)得更加完美。
總之,當(dāng)你需要在客戶端和服務(wù)端建立一個(gè)真正的低延遲的,接近實(shí)時(shí)連接的時(shí)候使用 WebSockets。記住這可能要求你重新考慮如何構(gòu)建服務(wù)器端程序,同時(shí)也需要你關(guān)注諸如事件隊(duì)列的技術(shù)。
如果你的使用場(chǎng)景要求顯示實(shí)時(shí)市場(chǎng)新聞,市場(chǎng)數(shù)據(jù),聊天程序等等,HTTP/2 + SSE 將會(huì)為你提供一個(gè)高效的雙向通信通道且你可以得到 HTTP 的所有益處:
當(dāng)考慮現(xiàn)有架構(gòu)的兼容性的時(shí)候,WebSockets 經(jīng)常會(huì)是一個(gè)痛點(diǎn),因?yàn)樯?jí) HTTP 連接到一個(gè)完全和 HTTP 不相關(guān)的協(xié)議。
可擴(kuò)展性和安全:網(wǎng)絡(luò)組件(防火墻,入侵檢測(cè),負(fù)載均衡器)的建立,維護(hù)和配置都是為 HTTP 所考慮的,大型/重要的程序會(huì)更喜歡具有彈性,安全和可伸縮性的環(huán)境。
同樣地,你不得不考慮瀏覽器兼容性。查看下 WebSocket 兼容情況:
兼容性還不錯(cuò)。
然而,HTTP/2 的情況就不太妙了:
僅支持 TLS(還不算壞)
僅限于 Windows 10 的 IE 11 部分支持
僅支持 OSX 10.11+ Safari 瀏覽器
僅當(dāng)你協(xié)商應(yīng)用 ALPN(服務(wù)器需要明確支持的東西)才會(huì)支持 HTTP/2
SSE 的支持情況要好些:
僅 IE/Edge 不支持。(好吧,Opera Mini 即不支持 SSE 也不支持 WebSockets,因此我們把它完全排隊(duì)在外)。有一些優(yōu)雅的墊片來(lái)讓 IE/Edge 支持 SSE。
SessionStack 是如何選擇的?SessionStack? 同時(shí)使用 WebSockets 和 HTTP,這取決于使用場(chǎng)景。
一旦整合 SessionStack 進(jìn)網(wǎng)頁(yè)程序,它會(huì)開始記錄 DOM 變化,用戶交互,JavaScript 異常,堆棧追蹤,失敗的網(wǎng)絡(luò)請(qǐng)求以及調(diào)試信息,允許你用視頻回放網(wǎng)頁(yè)程序中的問題及發(fā)生在用戶身上的一切事情。全部都是實(shí)時(shí)發(fā)生的并且要求對(duì)網(wǎng)頁(yè)程序不會(huì)產(chǎn)生任何的性能影響。
這意味著你可以實(shí)時(shí)加入到用戶會(huì)話,而用戶仍然在瀏覽器中。這樣的情況下,我們會(huì)選擇使用 HTTP,因?yàn)檫@并不需要雙向通信(服務(wù)端把數(shù)據(jù)傳輸?shù)綖g覽器端)。當(dāng)前情況下,使用 WebSocket 就是過(guò)度使用,難以維護(hù)和擴(kuò)展。
然而,整合進(jìn)網(wǎng)頁(yè)程序的 SessionStack 庫(kù)應(yīng)用了 WebSocket(優(yōu)先使用,否則回滾到 HTTP)。它會(huì)打包并且向我們的服務(wù)器發(fā)送數(shù)據(jù),這是單向通信。在這種情況下,之所以選擇 WebSocket 是因?yàn)橛?jì)劃中的某些產(chǎn)品功能可能需要進(jìn)行雙向通信。
本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94895.html
摘要:數(shù)據(jù)作為消息通過(guò)傳輸,每個(gè)消息由一個(gè)或多個(gè)幀組成,其中包含正在發(fā)送的數(shù)據(jù)有效負(fù)載。幀數(shù)據(jù)如上所述,數(shù)據(jù)可以被分割成多個(gè)幀。但是,規(guī)范希望能夠處理交錯(cuò)的控制幀。 文章底部分享給大家一套 react + socket 實(shí)戰(zhàn)教程 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第5篇。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過(guò)了前面的章...
摘要:注意值得注意的是,一旦接收到所有幀并且原始消息有效載荷已被重建,客戶端將僅被通知關(guān)于新消息。實(shí)驗(yàn)表明,在之后創(chuàng)建了第二個(gè)幀。以下值目前正在使用中代表繼續(xù)幀。 這一次,我們將深入到通信協(xié)議的世界中,對(duì)比并討論它們的屬性并構(gòu)建部件。我們將提供WebSockets和HTTP / 2的快速比較。 最后,我們分享一些關(guān)于如何選擇網(wǎng)絡(luò)協(xié)議。 概述 如今,擁有豐富動(dòng)態(tài)用戶界面的復(fù)雜網(wǎng)絡(luò)應(yīng)用程序被視為...
摘要:下面我們從前端基礎(chǔ)和底層原理開始講起。對(duì)于和這三個(gè)對(duì)應(yīng)于矢量圖位圖和圖的渲染來(lái)說(shuō),給前端開發(fā)帶來(lái)了重武器,很多小游戲也因此蓬勃發(fā)展。這篇文章受眾之大,后來(lái)被人重新整理并發(fā)布為,其中還包括中文版。 showImg(https://segmentfault.com/img/bVbjM5r?w=1142&h=640); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 這...
摘要:由兩部分組成回調(diào)函數(shù)和數(shù)據(jù)?;卣{(diào)函數(shù)是當(dāng)響應(yīng)到來(lái)時(shí)應(yīng)該在頁(yè)面中調(diào)用的函數(shù),回調(diào)函數(shù)的名字一般是在請(qǐng)求中指定的。下面是以個(gè)的例子回調(diào)函數(shù)的名字就是是通過(guò)動(dòng)態(tài)的元素來(lái)使用的,使用時(shí)可以為屬性指定一個(gè)跨域。是為與其他傳遞消息的很相似。 圖像Ping技術(shù) 根據(jù)一個(gè)網(wǎng)頁(yè)可以從任何網(wǎng)頁(yè)中加載圖像而不用擔(dān)心使用跨域的原理, 我們可以動(dòng)態(tài)的創(chuàng)建圖像, 使用他們的onload和onerror事件處理程序...
閱讀 3572·2021-11-22 15:22
閱讀 3361·2019-08-30 15:54
閱讀 2748·2019-08-30 15:53
閱讀 866·2019-08-29 11:22
閱讀 3567·2019-08-29 11:14
閱讀 2107·2019-08-26 13:46
閱讀 2238·2019-08-26 13:24
閱讀 2306·2019-08-26 12:22