使用伺服器傳送的事件串流更新

伺服器傳送事件 (SSE) 透過 HTTP 從伺服器傳送自動更新至用戶端 以獲得最佳效能和最安全的連線建立連線後,伺服器即可啟動資料 以及傳輸中

建議您使用 SSE 從網頁應用程式傳送推播通知。 SSE 會以單一方向傳送資訊,因此您不會在 用戶端。

瞭解 SSE 的概念。「訂閱」網頁應用程式到 更新是由伺服器產生的更新,每當有新事件發生時, 傳送至用戶端但為了確實瞭解伺服器傳送的事件 瞭解其 AJAX 前身的侷限性。包括:

  • 輪詢:應用程式會重複輪詢伺服器來取得資料。這項技巧 大部分的 Animation 應用程式都使用。透過 HTTP 通訊協定擷取 資料牽涉到要求和回應的格式用戶端發出要求 然後等待伺服器傳回資料。如果沒有可用路徑,就會留空 回應。額外的輪詢會增加 HTTP 負擔。

  • 長時間輪詢 (等待 GET / COMET):如果伺服器沒有資料 伺服器會將要求維持在開啟狀態,直到有新資料可用為止。 因此,這項技術通常稱為「懸掛 GET」。時間 伺服器回應、關閉連線 系統就會重複執行這個程序因此,伺服器會不斷回應 新的資料進行這類設定時,開發人員通常會使用 將指令碼標記改為「無限」iframe。

伺服器傳送事件從零開始設計,就是為了提升效率。 與 SSE 通訊時,伺服器可將資料推送至 不必請求初始要求。也就是 更新時也可從伺服器串流到用戶端。東南亞 開啟伺服器和用戶端之間的單一單向通道。

「伺服器發生的事件」與「長輪詢」之間的主要差異在於 SSE 都會直接由瀏覽器處理,使用者只需聽取訊息即可。

伺服器傳送事件與 WebSocket

為何您會選擇透過 WebSocket 使用伺服器傳送事件?就讓我來回答您的問題!

WebSockets 具有豐富的通訊協定, 雙向的全雙工通訊。雙向管道 遊戲、訊息應用程式以及任何您需要的應用實例,近乎即時更新 雙向轉送。

不過,有時您只需要從伺服器進行單向通訊。 例如,當朋友更新自己的狀態、股票代號、新聞動態消息或 和其他自動化資料推送機制也就是 用戶端 Web SQL 資料庫或 IndexedDB 物件儲存庫的更新。 如果您需要將資料傳送至伺服器,XMLHttpRequest 一律是好友。

透過 HTTP 傳送 SSE。沒有特殊的通訊協定或伺服器 才能正常運作WebSocket 需要全雙工 和新的 WebSocket 伺服器負責處理通訊協定。

此外,伺服器發生的事件具有許多 WebSocket 所缺乏的功能 支援自動重新連線、事件 ID 任意事件

使用 JavaScript 建立 EventSource

如要訂閱事件串流,請建立 EventSource 物件,並將 你的串流網址:

const source = new EventSource('stream.php');

接下來,請為 message 事件設定處理常式。您可以選擇 監聽 openerror

source.addEventListener('message', (e) => {
  console.log(e.data);
});

source.addEventListener('open', (e) => {
  // Connection was opened.
});

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

從伺服器推送更新時,onmessage 處理常式會觸發 且 e.data 資源中會提供新資料。神奇的功能是 瀏覽器會在連線關閉時,自動重新連線到 並在約 3 秒後擷取來源您的伺服器執行方式甚至可以掌控 重新連線逾時。

就是這麼簡單!您的客戶現在可以處理 stream.php 的事件了。

事件串流格式

建立 包含 text/event-stream 內容類型的純文字回應, 必須符合 SSE 格式 回應的基本格式應包含 data: 行,後接您的 訊息,後面加上兩個「\n」字元結束直播:

data: My message\n\n

多行資料

如果訊息較長,您可以使用多行 data: 程式碼來分割內容。 系統會將開頭為 data: 的兩行以上連續行視為 單一資料,這表示只會觸發一個 message 事件。

每一行必須以一個「\n」結尾(只有最後一個項目除外, 有兩個)。傳遞至 message 處理常式的結果是單一字串 以換行字元串連。例如:

data: first line\n
data: second line\n\n</pre>

這會產生「第一行\n 第二行」在「e.data」中。這樣一來,他們就能運用 e.data.split('\n').join(''):用於重建訊息 Sans「\n」字元。

傳送 JSON 資料

使用多行可協助您在不破壞語法的情況下傳送 JSON:

data: {\n
data: "msg": "hello world",\n
data: "id": 12345\n
data: }\n\n

還有可能用於處理該串流的用戶端程式碼:

source.addEventListener('message', (e) => {
  const data = JSON.parse(e.data);
  console.log(data.id, data.msg);
});

將 ID 與事件建立關聯

只要加入一行開頭為 id::

id: 12345\n
data: GOOG\n
data: 556\n\n

設定 ID 可讓瀏覽器追蹤已觸發的最後一個事件,如果 伺服器連線中斷,因此一個特殊 HTTP 標頭 (Last-Event-ID) 與新要求進行設定可讓瀏覽器判斷適合觸發的事件。 message 事件包含 e.lastEventId 屬性。

控管重新連線逾時

瀏覽器大約會在 3 秒內嘗試重新連線至來源 。若要調整逾時,您可以在名稱中加入 開頭為 retry:,後接毫秒數 才能重新連線。

下列範例會在 10 秒後嘗試重新連線:

retry: 10000\n
data: hello world\n\n

指定事件名稱

單一事件來源可能會納入 事件名稱。如果有開頭為 event: 的行, 後面加上事件的專屬名稱,即可將事件與該名稱建立關聯。 在用戶端上,您可以設定事件監聽器來監聽特定事件。

舉例來說,下列伺服器輸出內容會傳送三種類型的事件。 一般的「訊息」「事件」、「userlogon」和「update」事件:

data: {"msg": "First message"}\n\n
event: userlogon\n
data: {"username": "John123"}\n\n
event: update\n
data: {"username": "John123", "emotion": "happy"}\n\n

在用戶端上設定事件監聽器:

source.addEventListener('message', (e) => {
  const data = JSON.parse(e.data);
  console.log(data.msg);
});

source.addEventListener('userlogon', (e) => {
  const data = JSON.parse(e.data);
  console.log(`User login: ${data.username}`);
});

source.addEventListener('update', (e) => {
  const data = JSON.parse(e.data);
  console.log(`${data.username} is now ${data.emotion}`);
};

伺服器範例

以下是以 PHP 編寫的基本伺服器實作:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.

/**
* Constructs the SSE data format and flushes that data to the client.
*
* @param string $id Timestamp/id of this connection.
* @param string $msg Line of text that should be transmitted.
**/

function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

$serverTime = time();

sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
?>

以下說明如何使用 Node JS Express 處理常式:

app.get('/events', (req, res) => {
    // Send the SSE header.
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    // Sends an event to the client where the data is the current date,
    // then schedules the event to happen again after 5 seconds.
    const sendEvent = () => {
        const data = (new Date()).toLocaleTimeString();
        res.write("data: " + data + '\n\n');
        setTimeout(sendEvent, 5000);
    };

    // Send the initial event immediately.
    sendEvent();
});

sse-node.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <script>
    const source = new EventSource('/events');
    source.onmessage = (e) => {
        const content = document.createElement('div');
        content.textContent = e.data;
        document.body.append(content);
    };
    </script>
  </body>
</html>

取消事件串流

一般來說,瀏覽器在連線時,會自動重新連線至事件來源 但用戶端或伺服器可取消該行為。

如要從用戶端取消串流,請呼叫:

source.close();

如要從伺服器取消串流,請使用非 text/event-stream 回應 Content-Type 或傳回 200 OK 以外的 HTTP 狀態 (例如 404 Not Found)。

這兩種方法都會造成瀏覽器無法重新建立連線。

安全性須知

EventSource 產生的要求必須遵循與 擷取其他網路 API,例如擷取作業如果伺服器需要 SSE 端點 以及如何從不同來源存取資源,瞭解如何啟用 跨源資源共享 (CORS)