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