HTTP 服務器推送也稱 HTTP 流,是一種客戶端-服務器通訊模式,它將信息從 HTTP 服務器異步推送到客戶端,而無需客戶端請求。如今的 web 和 app 中,愈來愈多的場景使用這種通訊模式,好比實時的消息提醒,IM在線聊天,多人文檔協做等。之前實現這種相似的功能通常都是用ajax長輪詢,而如今咱們有了新的、更優雅的選擇 —— WebSocket 和 SSE。javascript
WebSocket是HTML5開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。在WebSocket API中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。瀏覽器經過 JavaScript 向服務器發出創建 WebSocket 鏈接的請求,鏈接創建之後,客戶端和服務器端就能夠經過 TCP 鏈接直接交換數據。當你獲取 Web Socket 鏈接後,你能夠經過 send() 方法來向服務器發送數據,並經過 onmessage 事件來接收服務器返回的數據。java
SSE 是 Server-Sent Events 的簡稱, 是一種服務器端到客戶端(瀏覽器)的單項消息推送。對應的瀏覽器端實現 Event Source 接口被制定爲HTML5 的一部分。不過如今IE不支持該技術。相比於 WebSocket,SSE 簡單不少,服務器端和客戶端工做量都要小不少、簡單不少,同時實現的功能也要有侷限。node
[Constructor(DOMString url, optional EventSourceInit eventSourceInitDict)]
interface EventSource : EventTarget {
readonly attribute DOMString url;
readonly attribute boolean withCredentials;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSED = 2;
readonly attribute unsigned short readyState;
// networking
attribute EventHandler onopen;
attribute EventHandler onmessage;
attribute EventHandler onerror;
void close();
};
dictionary EventSourceInit {
boolean withCredentials = false;
};
複製代碼
var source = new EventSource(url)
複製代碼
// 創建鏈接後,觸發`open` 事件
source.onopen = (event)=>{
// ...
}
// 收到消息,觸發`message` 事件
source.onmessage = (event)=>{
// ...
}
// 發生錯誤,觸發`error` 事件
source.onerror = (event)=>{
// ...
}
// 自定義事件
source.addEventListener('eventName', event => {
// ...
}, false)
複製代碼
source.close()
複製代碼
SSE的相應,須要設置以下的Http頭信息web
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
複製代碼
第一行,Content-Type
指定的 MIME
類型必須爲 text/event-stream
ajax
SSE 推送的消息必須是UTF-8
編碼的純文本。每次推送有若干個事件消息組成,每一個事件消息之間用兩個換行(\n\n
)分割。每一個事件消息又有若干行組成,每行格式以鍵值對形式組成:express
[key]: value\n
複製代碼
key
有一下幾個值可取瀏覽器
addEventListener()
監聽該事件。瀏覽器端代碼bash
// index.js
var source = new EventSource('/stream');
source.onmessage = function(event) {
var message = event.data;
// do stuff based on received message
};
複製代碼
服務器端代碼(nodejs)服務器
var express = require('express')
var fs = require('fs')
var app = express()
app.get('/stream', (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive"
});
var interval = setInterval(function () {
res.write("data: " + (new Date()) + "\n\n");
}, 1000);
req.connection.addListener("close", function () {
clearInterval(interval);
}, false);
})
app.listen(9999, (err) => {
if (err) {
console.log(err)
return
}
console.log('listening on port 9999')
})
複製代碼