SSE(Server Send Event)使用方法——來源於阮一峰所著《JavaScript 標準參考教程(alpha)》
JavaScript 標準參考教程(alpha)
Web API
Server-Sent Events
GitHub
TOP
Server-Sent Events
來自《JavaScript 標準參考教程(alpha)》,by 阮一峰
1. 目錄
簡介
與 WebSocket 的比較
客戶端 API
EventSource 物件
readyState 屬性
url 屬性
withCredentials 屬性
onopen 屬性
onmessage 屬性
onerror 屬性
自定義事件
close() 方法
伺服器實現
資料格式
data 欄位
id 欄位
event 欄位
retry 欄位
Node 伺服器例項
參考連結
2. 簡介
伺服器向客戶端推送資料,有很多解決方案。除了“輪詢” 和 WebSocket,HTML 5 還提供了 Server-Sent Events(以下簡稱 SSE)。
一般來說,HTTP 協議只能客戶端向伺服器發起請求,伺服器不能主動向客戶端推送。但是有一種特殊情況,就是伺服器向客戶端宣告,接下來要傳送的是流資訊(streaming)。也就是說,傳送的不是一次性的資料包,而是一個數據流,會連續不斷地傳送過來。這時,客戶端不會關閉連線,會一直等著伺服器發過來的新的資料流。本質上,這種通訊就是以流資訊的方式,完成一次用時很長的下載。
SSE 就是利用這種機制,使用流資訊向瀏覽器推送資訊。它基於 HTTP 協議,目前除了 IE/Edge,其他瀏覽器都支援。
3. 與 WebSocket 的比較
SSE 與 WebSocket 作用相似,都是建立瀏覽器與伺服器之間的通訊渠道,然後伺服器向瀏覽器推送資訊。
總體來說,WebSocket 更強大和靈活。因為它是全雙工通道,可以雙向通訊;SSE 是單向通道,只能伺服器向瀏覽器傳送,因為 streaming 本質上就是下載。如果瀏覽器向伺服器傳送資訊,就變成了另一次 HTTP 請求。
但是,SSE 也有自己的優點。
SSE 使用 HTTP 協議,現有的伺服器軟體都支援。WebSocket 是一個獨立協議。
SSE 屬於輕量級,使用簡單;WebSocket 協議相對複雜。
SSE 預設支援斷線重連,WebSocket 需要自己實現。
SSE 一般只用來傳送文字,二進位制資料需要編碼後傳送,WebSocket 預設支援傳送二進位制資料。
SSE 支援自定義傳送的訊息型別。
因此,兩者各有特點,適合不同的場合。
4. 客戶端 API
EventSource 物件
SSE 的客戶端 API 部署在EventSource物件上。下面的程式碼可以檢測瀏覽器是否支援 SSE。
if (‘EventSource’ in window) {
// …
}
使用 SSE 時,瀏覽器首先生成一個EventSource例項,向伺服器發起連線。
var source = new EventSource(url);
上面的url可以與當前網址同域,也可以跨域。跨域時,可以指定第二個引數,開啟withCredentials屬性,表示是否一起傳送 Cookie。
var source = new EventSource(url, { withCredentials: true });
readyState 屬性
EventSource例項的readyState屬性,表明連線的當前狀態。該屬性只讀,可以取以下值。
0:相當於常量EventSource.CONNECTING,表示連線還未建立,或者斷線正在重連。
1:相當於常量EventSource.OPEN,表示連線已經建立,可以接受資料。
2:相當於常量EventSource.CLOSED,表示連線已斷,且不會重連。
var source = new EventSource(url);
console.log(source.readyState);
url 屬性
EventSource例項的url屬性返回連線的網址,該屬性只讀。
withCredentials 屬性
EventSource例項的withCredentials屬性返回一個布林值,表示當前例項是否開啟 CORS 的withCredentials。該屬性只讀,預設是false。
onopen 屬性
連線一旦建立,就會觸發open事件,可以在onopen屬性定義回撥函式。
source.onopen = function (event) {
// …
};
// 另一種寫法
source.addEventListener(‘open’, function (event) {
// …
}, false);
onmessage 屬性
客戶端收到伺服器發來的資料,就會觸發message事件,可以在onmessage屬性定義回撥函式。
source.onmessage = function (event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
};
// 另一種寫法
source.addEventListener(‘message’, function (event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
}, false);
上面程式碼中,引數物件event有如下屬性。
data:伺服器端傳回的資料(文字格式)。
origin: 伺服器 URL 的域名部分,即協議、域名和埠,表示訊息的來源。
lastEventId:資料的編號,由伺服器端傳送。如果沒有編號,這個屬性為空。
onerror 屬性
如果發生通訊錯誤(比如連線中斷),就會觸發error事件,可以在onerror屬性定義回撥函式。
source.onerror = function (event) {
// handle error event
};
// 另一種寫法
source.addEventListener(‘error’, function (event) {
// handle error event
}, false);
自定義事件
預設情況下,伺服器發來的資料,總是觸發瀏覽器EventSource例項的message事件。開發者還可以自定義 SSE 事件,這種情況下,傳送回來的資料不會觸發message事件。
source.addEventListener(‘foo’, function (event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
}, false);
上面程式碼中,瀏覽器對 SSE 的foo事件進行監聽。如何實現伺服器傳送foo事件,請看下文。
close() 方法
close方法用於關閉 SSE 連線。
source.close();
5. 伺服器實現
資料格式
伺服器向瀏覽器傳送的 SSE 資料,必須是 UTF-8 編碼的文字,具有如下的 HTTP 頭資訊。
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
上面三行之中,第一行的Content-Type必須指定 MIME 型別為event-steam。
每一次傳送的資訊,由若干個message組成,每個message之間用\n\n分隔。每個message內部由若干行組成,每一行都是如下格式。
field: value\n
上面的field可以取四個值。
data
event
id
retry
此外,還可以有冒號開頭的行,表示註釋。通常,伺服器每隔一段時間就會向瀏覽器傳送一個註釋,保持連線不中斷。
: This is a comment
下面是一個例子。
: this is a test stream\n\n
data: some text\n\n
data: another message\n
data: with two lines \n\n
data 欄位
資料內容用data欄位表示。
data: message\n\n
如果資料很長,可以分成多行,最後一行用\n\n結尾,前面行都用\n結尾。
data: begin message\n
data: continue message\n\n
下面是一個傳送 JSON 資料的例子。
data: {\n
data: “foo”: “bar”,\n
data: “baz”, 555\n
data: }\n\n
id 欄位
資料識別符號用id欄位表示,相當於每一條資料的編號。
id: msg1\n
data: message\n\n
瀏覽器用lastEventId屬性讀取這個值。一旦連線斷線,瀏覽器會發送一個 HTTP 頭,裡面包含一個特殊的Last-Event-ID頭資訊,將這個值傳送回來,用來幫助伺服器端重建連線。因此,這個頭資訊可以被視為一種同步機制。
event 欄位
event欄位表示自定義的事件型別,預設是message事件。瀏覽器可以用addEventListener()監聽該事件。
event: foo\n
data: a foo event\n\n
data: an unnamed event\n\n
event: bar\n
data: a bar event\n\n
上面的程式碼創造了三條資訊。第一條的名字是foo,觸發瀏覽器的foo事件;第二條未取名,表示預設型別,觸發瀏覽器的message事件;第三條是bar,觸發瀏覽器的bar事件。
下面是另一個例子。
event: userconnect
data: {“username”: “bobby”, “time”: “02:33:48”}
event: usermessage
data: {“username”: “bobby”, “time”: “02:34:11”, “text”: “Hi everyone.”}
event: userdisconnect
data: {“username”: “bobby”, “time”: “02:34:23”}
event: usermessage
data: {“username”: “sean”, “time”: “02:34:36”, “text”: “Bye, bobby.”}
retry 欄位
伺服器可以用retry欄位,指定瀏覽器重新發起連線的時間間隔。
retry: 10000\n
兩種情況會導致瀏覽器重新發起連線:一種是時間間隔到期,二是由於網路錯誤等原因,導致連接出錯。
Node 伺服器例項
SSE 要求伺服器與瀏覽器保持連線。對於不同的伺服器軟體來說,所消耗的資源是不一樣的。Apache 伺服器,每個連線就是一個執行緒,如果要維持大量連線,勢必要消耗大量資源。Node 則是所有連線都使用同一個執行緒,因此消耗的資源會小得多,但是這要求每個連線不能包含很耗時的操作,比如磁碟的 IO 讀寫。
下面是 Node 的 SSE 伺服器例項。
var http = require(“http”);
http.createServer(function (req, res) {
var fileName = “.” + req.url;
if (fileName === “./stream”) {
res.writeHead(200, {
“Content-Type”:”text/event-stream”,
“Cache-Control”:”no-cache”,
“Connection”:”keep-alive”,
“Access-Control-Allow-Origin”: ‘*’,
});
res.write(“retry: 10000\n”);
res.write(“event: connecttime\n”);
res.write(“data: ” + (new Date()) + “\n\n”);
res.write(“data: ” + (new Date()) + “\n\n”);
interval = setInterval(function () {
res.write(“data: ” + (new Date()) + “\n\n”);
}, 1000);
req.connection.addListener(“close”, function () {
clearInterval(interval);
}, false);
}
}).listen(8844, “127.0.0.1”);
參考連結
Colin Ihrig, Implementing Push Technology Using Server-Sent Events
Colin Ihrig,The Server Side of Server-Sent Events
Eric Bidelman, Stream Updates with Server-Sent Events
MDN,Using server-sent events
Segment.io, Server-Sent Events: The simplest realtime browser spec
留言
comments powered by Disqus
版權宣告 | last modified on 2013-01-07