W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
Server-Sent Events 規(guī)范描述了一個(gè)內(nèi)建的類 ?EventSource
?,它能保持與服務(wù)器的連接,并允許從中接收事件。
與 WebSocket
類似,其連接是持久的。
但是兩者之間有幾個(gè)重要的區(qū)別:
WebSocket
|
EventSource
|
---|---|
雙向:客戶端和服務(wù)端都能交換消息 | 單向:僅服務(wù)端能發(fā)送消息 |
二進(jìn)制和文本數(shù)據(jù) | 僅文本數(shù)據(jù) |
WebSocket 協(xié)議 | 常規(guī) HTTP 協(xié)議 |
與 WebSocket
相比,EventSource
是與服務(wù)器通信的一種不那么強(qiáng)大的方式。
我們?yōu)槭裁匆褂盟?
主要原因:簡(jiǎn)單。在很多應(yīng)用中,WebSocket
有點(diǎn)大材小用。
我們需要從服務(wù)器接收一個(gè)數(shù)據(jù)流:可能是聊天消息或者市場(chǎng)價(jià)格等。這正是 EventSource
所擅長(zhǎng)的。它還支持自動(dòng)重新連接,而在 WebSocket
中這個(gè)功能需要我們手動(dòng)實(shí)現(xiàn)。此外,它是一個(gè)普通的舊的 HTTP,不是一個(gè)新協(xié)議。
要開(kāi)始接收消息,我們只需要?jiǎng)?chuàng)建 new EventSource(url)
即可。
瀏覽器將會(huì)連接到 url
并保持連接打開(kāi),等待事件。
服務(wù)器響應(yīng)狀態(tài)碼應(yīng)該為 200,header 為 Content-Type: text/event-stream
,然后保持此連接并以一種特殊的格式寫入消息,就像這樣:
data: Message 1
data: Message 2
data: Message 3
data: of two lines
data:
? 后為消息文本,冒號(hào)后面的空格是可選的。\n\n
? 分隔。\n
?,我們可以在要換行的位置立即再發(fā)送一個(gè) ?data:
?(上面的第三條消息)。在實(shí)際開(kāi)發(fā)中,復(fù)雜的消息通常是用 JSON 編碼后發(fā)送。換行符在其中編碼為 \n
,因此不需要多行 data:
消息。
例如:
data: {"user":"John","message":"First line\n Second line"}
……因此,我們可以假設(shè)一個(gè) data:
只保存了一條消息。
對(duì)于每個(gè)這樣的消息,都會(huì)生成 message
事件:
let eventSource = new EventSource("/events/subscribe");
eventSource.onmessage = function(event) {
console.log("New message", event.data);
// 對(duì)于上面的數(shù)據(jù)流將打印三次
};
// 或 eventSource.addEventListener('message', ...)
EventSource
支持跨源請(qǐng)求,就像 fetch
和任何其他網(wǎng)絡(luò)方法。我們可以使用任何 URL:
let source = new EventSource("https://another-site.com/events");
遠(yuǎn)程服務(wù)器將會(huì)獲取到 Origin
header,并且必須以 Access-Control-Allow-Origin
響應(yīng)來(lái)處理。
要傳遞憑證(credentials),我們應(yīng)該設(shè)置附加選項(xiàng) withCredentials
,就像這樣:
let source = new EventSource("https://another-site.com/events", {
withCredentials: true
});
更多關(guān)于跨源 header 的詳細(xì)內(nèi)容,請(qǐng)參見(jiàn) Fetch:跨源請(qǐng)求。
創(chuàng)建之后,new EventSource
連接到服務(wù)器,如果連接斷開(kāi) —— 則重新連接。
這非常方便,我們不用去關(guān)心重新連接的事情。
每次重新連接之間有一點(diǎn)小的延遲,默認(rèn)為幾秒鐘。
服務(wù)器可以使用 retry:
來(lái)設(shè)置需要的延遲響應(yīng)時(shí)間(以毫秒為單位)。
retry: 15000
data: Hello, I set the reconnection delay to 15 seconds
retry:
既可以與某些數(shù)據(jù)一起出現(xiàn),也可以作為獨(dú)立的消息出現(xiàn)。
在重新連接之前,瀏覽器需要等待那么多毫秒。甚至更長(zhǎng),例如,如果瀏覽器知道(從操作系統(tǒng))此時(shí)沒(méi)有網(wǎng)絡(luò)連接,它會(huì)等到連接出現(xiàn),然后重試。
eventSource.close()
?:let eventSource = new EventSource(...);
eventSource.close();
并且,如果響應(yīng)具有不正確的 Content-Type
或者其 HTTP 狀態(tài)碼不是 301,307,200 和 204,則不會(huì)進(jìn)行重新連接。在這種情況下,將會(huì)發(fā)出 "error"
事件,并且瀏覽器不會(huì)重新連接。
請(qǐng)注意:
當(dāng)連接最終被關(guān)閉時(shí),就無(wú)法“重新打開(kāi)”它。如果我們想要再次連接,只需要?jiǎng)?chuàng)建一個(gè)新的
EventSource
。
當(dāng)一個(gè)連接由于網(wǎng)絡(luò)問(wèn)題而中斷時(shí),客戶端和服務(wù)器都無(wú)法確定哪些消息已經(jīng)收到哪些沒(méi)有收到。
為了正確地恢復(fù)連接,每條消息都應(yīng)該有一個(gè) ?id
? 字段,就像這樣:
data: Message 1
id: 1
data: Message 2
id: 2
data: Message 3
data: of two lines
id: 3
當(dāng)收到具有 id
的消息時(shí),瀏覽器會(huì):
eventSource.lastEventId
? 設(shè)置為其值。id
? 的 header ?Last-Event-ID
?,以便服務(wù)器可以重新發(fā)送后面的消息。把 ?
id:
? 放在 ?data:
? 后請(qǐng)注意:
id
被服務(wù)器附加到data
消息后,以確保在收到消息后lastEventId
會(huì)被更新。
EventSource
對(duì)象有 readyState
屬性,該屬性具有下列值之一:
EventSource.CONNECTING = 0; // 連接中或者重連中
EventSource.OPEN = 1; // 已連接
EventSource.CLOSED = 2; // 連接已關(guān)閉
對(duì)象創(chuàng)建完成或者連接斷開(kāi)后,它始終是 EventSource.CONNECTING
(等于 0
)。
我們可以查詢?cè)搶傩砸粤私?nbsp;EventSource
的狀態(tài)。
默認(rèn)情況下 ?EventSource
? 對(duì)象生成三個(gè)事件:
message
? —— 收到消息,可以用 ?event.data
? 訪問(wèn)。open
? —— 連接已打開(kāi)。error
? —— 無(wú)法建立連接,例如,服務(wù)器返回 HTTP 500 狀態(tài)碼。服務(wù)器可以在事件開(kāi)始時(shí)使用 event: ...
指定另一種類型事件。
例如:
event: join
data: Bob
data: Hello
event: leave
data: Bob
要處理自定義事件,我們必須使用 addEventListener
而非 onmessage
:
eventSource.addEventListener('join', event => {
alert(`Joined ${event.data}`);
});
eventSource.addEventListener('message', event => {
alert(`Said: ${event.data}`);
});
eventSource.addEventListener('leave', event => {
alert(`Left ${event.data}`);
});
服務(wù)器依次發(fā)送 1
,2
,3
,最后發(fā)送 bye
并斷開(kāi)連接。
然后瀏覽器會(huì)自動(dòng)重新連接。
?EventSource
? 對(duì)象自動(dòng)建立一個(gè)持久的連接,并允許服務(wù)器通過(guò)這個(gè)連接發(fā)送消息。
它提供了:
retry
? 超時(shí)內(nèi)自動(dòng)重新連接。Last-Event-ID
? header 中發(fā)送出去。readyState
? 屬性中。這使得 EventSource
成為 WebSocket
的一個(gè)可行的替代方案,因?yàn)?nbsp;WebSocket
更底層(low-level),且缺乏這樣的內(nèi)建功能(盡管它們可以被實(shí)現(xiàn))。
在很多實(shí)際應(yīng)用中,EventSource
的功能就已經(jīng)夠用了。
EventSource
在所有現(xiàn)代瀏覽器(除了 IE)中都得到了支持。
語(yǔ)法:
let source = new EventSource(url, [credentials]);
第二個(gè)參數(shù)只有一個(gè)可選項(xiàng):{ withCredentials: true }
,它允許發(fā)送跨源憑證。
總體跨源安全性與 fetch
以及其他網(wǎng)絡(luò)方法相同。
?readyState
?
當(dāng)前連接狀態(tài):為 ?EventSource.CONNECTING (=0)
?,?EventSource.OPEN (=1)
?,?EventSource.CLOSED (=2)
? 三者之一。
?lastEventId
?
最后接收到的 ?id
?。重新連接后,瀏覽器在 header ?Last-Event-ID
? 中發(fā)送此 id。
?close()
?
關(guān)閉連接。
?message
?
接收到的消息,消息數(shù)據(jù)在 ?event.data
? 中。
?open
?
連接已建立。
?error
?
如果發(fā)生錯(cuò)誤,包括連接丟失(將會(huì)自動(dòng)重連)以及其他致命錯(cuò)誤。我們可以檢查 ?readyState
? 以查看是否正在嘗試重新連接。
服務(wù)器可以在 event:
中設(shè)置自定義事件名稱。應(yīng)該使用 addEventListener
來(lái)處理此類事件,而不是使用 on<event>
。
服務(wù)器發(fā)送由 \n\n
分隔的消息。
一條消息可能有以下字段:
data:
? —— 消息體(body),一系列多個(gè) ?data
? 被解釋為單個(gè)消息,各個(gè)部分之間由 ?\n
? 分隔。id:
? —— 更新 ?lastEventId
?,重連時(shí)以 ?Last-Event-ID
? 發(fā)送此 id。retry:
? —— 建議重連的延遲,以 ms 為單位。無(wú)法通過(guò) JavaScript 進(jìn)行設(shè)置。event:
? —— 事件名,必須在 ?data:
? 之前。一條消息可以按任何順序包含一個(gè)或多個(gè)字段,但是 id:
通常排在最后。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: