服務(wù)器端推送技術(shù)

源文件:https://w3ctech.com/topic/1754

技術(shù)實現(xiàn)

1、ajax輪詢

2、ajax長輪詢

3、websocket

4、server-sent-events(SSE)

ajax輪詢

建議不采用,通過 setInterval 定時發(fā)送請求。

缺點(diǎn):數(shù)據(jù)同步不及時;增加后端處理壓力。

setInterval(function() {

?????$.ajax({?

?????????url: 'http://api.3g.qq.com',?

? ? ? ? ?success: function() { //code from here }?

?????});

}, 3000);


ajax長輪詢

沒有更新的時候不再返回空響應(yīng),而且把連接保持到有更新的時候,客戶端向服務(wù)器發(fā)送Ajax請求,服務(wù)器接到請求后hold住連接,直到有新消息才返回響應(yīng)信息并關(guān)閉連接,客戶端處理完響應(yīng)信息后再向服務(wù)器發(fā)送新的請求,通常把這種實現(xiàn)也叫做comet。

function async() {?

?????$.ajax({?

?????????url: 'http://api.3g.qq.com',

?????????success: function() { async(); //code from here }?

?????});

}

通常的做法是,在服務(wù)器的程序中加入一個死循環(huán),在循環(huán)中監(jiān)測數(shù)據(jù)的變動。當(dāng)發(fā)現(xiàn)新數(shù)據(jù)時,立即將其輸出給瀏覽器并斷開連接,瀏覽器在收到數(shù)據(jù)后,再次發(fā)起請求以進(jìn)入下一個周期。


Server-sent-events(SSE)

Server-sent-events(SSE)讓服務(wù)端可以向客戶端流式發(fā)送文本消息,在實現(xiàn)上,客戶端瀏覽器中增加EventSource對象,使其能通過事件的方式接收到服務(wù)器推送的消息,在服務(wù)端,使用長連接的事件流協(xié)議,即請求響應(yīng)時增加新數(shù)據(jù)流數(shù)據(jù)格式。

非常適應(yīng)于后端數(shù)據(jù)更新頻繁且對實時性要求較高而又不需要客戶端向服務(wù)端通信的場景下。

EventSource API

var source = new EventSource('http://localhost:8080');

source.addEventListener('message', function(e) {? console.log(e.data);}, false);

source.addEventListener('open', function(e) {? // Connection was opened.}, false);

source.addEventListener('error', function(e) {? if (e.readyState == EventSource.CLOSED) {? ? // Connection was closed.? }}, false);

source.addEventListener('userlogin', function(e) {? console.log(e.data);}, false);

客戶端API使用非常簡單,瀏覽器在支持的情況下會自動處理一切,包括連接管理接收并解析數(shù)據(jù)到最后觸發(fā)DOM事件,開發(fā)時只需要關(guān)注業(yè)務(wù)邏輯,EventSource接口還能自動重新連接并跟蹤最近接收的消息,還可以向服務(wù)器發(fā)送上一次接收到消息的ID,以便服務(wù)器重傳丟失的消息并恢復(fù)流。

Event Stream協(xié)議

SSE事件流以流式HTTP響應(yīng)請求,客戶端發(fā)起普通的HTTP請求,服務(wù)器以自定義的text/event-stream內(nèi)容類型響應(yīng),然后通過事件傳遞數(shù)據(jù)。

響應(yīng)頭

Content-Type: text/event-stream

Cache-Control: no-cache

Connection: keep-alive

響應(yīng)數(shù)據(jù)格式

id: 123\n

retry: 10000\n

event: userlogin\n

data: {"username": "John123"}\n\n

客戶端通過EventSource接口發(fā)起連接,服務(wù)器以text/event-stream內(nèi)容類型響應(yīng),可設(shè)置中斷后重連時間間隔retry,數(shù)據(jù)通過字符串的方式賦值給data字段,也可以指定消息類型給event字段。在客戶端EventSource接口通過檢查換行分隔符來解析到來的數(shù)據(jù)流,從data字段中獲取數(shù)據(jù),檢查可選的ID和類型,最后分配事件告知應(yīng)用,如果存在某個類型,觸發(fā)自定義的事件回調(diào),否則就會調(diào)用通用的onmessage回調(diào)。

為了在連接中斷時恢復(fù)中斷過程中丟失的消息,服務(wù)器在響應(yīng)時可以給每條消息關(guān)聯(lián)任意的ID字符串,瀏覽器會自動記錄最后一次接收到消息ID,并在發(fā)送重新連接請求時自動在HTTP請求頭中追加Last-Event-ID,這樣就可以標(biāo)識中斷過程中丟失的消息并重新發(fā)送給客戶端。

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

基于現(xiàn)有http協(xié)議,實現(xiàn)簡單

斷開后自動重聯(lián),并可設(shè)置重聯(lián)超時

派發(fā)任意事件

跨域并有相應(yīng)的安全過濾

缺點(diǎn)

只能單向通信,服務(wù)器端向客戶端推送事件

事件流協(xié)議只能傳輸U(kuò)TF-8數(shù)據(jù),不支持二進(jìn)制流。

IE下目前所有不支持EventSource

Tip?如果代理服務(wù)器或中間設(shè)備不支持SSE,會導(dǎo)致連接失效,正式環(huán)境中使用通過TLS協(xié)議發(fā)送SSE事件流。


WebSocket

WebSocket可以實現(xiàn)與客戶端與服務(wù)器雙向,基于消息的文本或二進(jìn)制數(shù)據(jù)通信,主要包括兩個部分,客戶端WebSocket API及WebSocket協(xié)議。

WebSocket是HTML5出的東西(協(xié)議),也就是說HTTP協(xié)議沒有變化,或者說沒關(guān)系,但HTTP是不支持持久連接的(長連接,循環(huán)連接的不算)首先HTTP有1.1和1.0之說,也就是所謂的keep-alive,把多個HTTP請求合并為一個,但是Websocket其實是一個新協(xié)議,跟HTTP協(xié)議基本沒有關(guān)系,只是為了兼容現(xiàn)有瀏覽器的握手規(guī)范而已。

WebSocket API

瀏覽器提供的WebSocket API很簡單,使用時無需關(guān)心連接管理和消息處理等底層細(xì)節(jié),只需要發(fā)起連接,綁定相應(yīng)的事件回調(diào)即可。

var connection = new WebSocket('ws://localhost:8080');

// When the connection is open, send some data to the server

connection.onopen = function () { connection.send('Ping'); // Send the message 'Ping' to the server};

// Log errors

connection.onerror = function (error) { console.log('WebSocket Error ' + error);};

// Log messages from the server

connection.onmessage = function (e) { console.log('Server: ' + e.data);};

// Sending String

connection.send('your message');

// Sending canvas ImageData as ArrayBuffer

var img = canvas_context.getImageData(0, 0, 400, 320);

var binary = new Uint8Array(img.data.length);

for (var i = 0; i < img.data.length; i++) {

????binary[i] = img.data[i];

}

connection.send(binary.buffer);

// Sending file as Blob

var file = document.querySelector('input[type="file"]').files[0];

connection.send(file);

WebSocket資源URL采用了自定議模式,沒有使用http是為了在非http協(xié)議場景下也能使用,wss表示使用加密信道通信(TCP + TLS),支持接收和發(fā)送文本和二進(jìn)制數(shù)據(jù)。

WebSocket 協(xié)議

WebSocket通信協(xié)議包含兩個部分,一是開放性HTTP握手連接協(xié)商連接參數(shù),二是二進(jìn)制消息分幀機(jī)制(接收消息的文本和二進(jìn)制數(shù)據(jù)傳輸)。它是一個獨(dú)立完善的協(xié)議,也可以在瀏覽器之外實現(xiàn)。

HTTP升級協(xié)商

WebSocket協(xié)議提供了很多強(qiáng)大的特性:基于消息的通信、自定義的二進(jìn)制分幀層、子協(xié)議協(xié)商、可選的協(xié)議擴(kuò)展,等等。即在交換數(shù)據(jù)之前,客戶端必須與服務(wù)器協(xié)商適當(dāng)?shù)膮?shù)以建立連接。

利用HTTP完成握手有幾個好處。首先,讓W(xué)ebSockets與現(xiàn)有HTTP基礎(chǔ)設(shè)施兼容:WebSocket服務(wù)器可以運(yùn)行在80和443 端口上,這通常是對客戶端唯一開放的端口。其次,讓我們可以重用并擴(kuò)展HTTP的Upgrade流,為其添加自定義的WebSocket首部,以完成協(xié)商。

請求頭信息

Connection:Upgrade

Sec-WebSocket-Key:eDCPPyPQZq7PiwRcx8SPog==

Sec-WebSocket-Version:13

Upgrade:websocket

響應(yīng)頭信息

HTTP/1.1 101 Switching Protocols

Connection:Upgrade

Sec-WebSocket-Accept:/ZVAP3n6XuqDUoDp416PYUO+ZJc=

Upgrade:websocket

最后,前述握手完成后,如果握手成功,該連接就可以用作雙向通信信道交換WebSocket消息。到此,客戶端與服務(wù)器之間不會再發(fā)生HTTP通信,一切由WebSocket?協(xié)議接管。

服務(wù)端實現(xiàn)

。Node (Socket.IO, WebSocket-Node, ws)

。Java (Jetty)

。Python (Tornado, pywebsocket)

。...

使用場景

適合于對數(shù)據(jù)的實時性要求比較強(qiáng)的場景,如通信、股票、Feed、直播、共享桌面,特別適合于客戶端與服務(wù)頻繁交互的情況下,如實時共享、多人協(xié)作等平臺。

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

。真正的全雙工通信

。支持跨域設(shè)置(Access-Control-Allow-Origin)

缺點(diǎn)

。采用新的協(xié)議,后端需要單獨(dú)實現(xiàn)

。客戶端并不是所有瀏覽器都支持

。代理服務(wù)器會有不支持websocket的情況

。無超時處理

。更耗電及占用資源


TIP?代理、很多現(xiàn)有的HTTP中間設(shè)備可能不理解新的WebSocket協(xié)議,而這可能導(dǎo)致各種問題,使用時需要注意,可以使借助TLS,通過建立一條端到端的加密信道,可以讓W(xué)ebSocket通信繞過所有中間代理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容