The サーバー送信イベント 仕様書は、サーバーとの接続を維持し、サーバーからイベントを受信することを可能にする組み込みクラスEventSourceについて説明しています。
WebSocketと同様に、接続は永続的です。
しかし、いくつかの重要な違いがあります。
WebSocket |
EventSource |
|---|---|
| 双方向:クライアントとサーバーの両方がメッセージを交換できます。 | 一方向:サーバーのみがデータを送信します。 |
| バイナリデータとテキストデータ | テキストのみ |
| WebSocketプロトコル | 通常のHTTP |
EventSourceは、WebSocketよりもサーバーとの通信能力が低い方法です。
なぜこれを使うべきなのでしょうか?
主な理由は、それがよりシンプルであるということです。多くのアプリケーションでは、WebSocketのパワーは少し多すぎます。
サーバーからデータストリームを受信する必要があります。チャットメッセージや市場価格などです。それがEventSourceの長所です。また、自動再接続をサポートしており、WebSocketでは手動で実装する必要があります。さらに、新しいプロトコルではなく、従来のHTTPです。
メッセージの受信
メッセージの受信を開始するには、new EventSource(url)を作成するだけです。
ブラウザはurlに接続し、接続をオープンに保ち、イベントを待ちます。
サーバーはステータス200とヘッダーContent-Type: text/event-streamで応答し、接続を維持し、次の特別な形式でメッセージを書き込む必要があります。
data: Message 1
data: Message 2
data: Message 3
data: of two lines
- メッセージテキストは
data:の後に続きます。コロンの後のスペースはオプションです。 - メッセージは二重改行
\n\nで区切られます。 - 改行
\nを送信するには、すぐに別のdata:を送信できます(上記の3番目のメッセージ)。
実際には、複雑なメッセージは通常JSONでエンコードされて送信されます。改行は\nとしてエンコードされるため、複数行のdata:メッセージは必要ありません。
例えば
data: {"user":"John","message":"First line\n Second line"}
…したがって、1つのdata:が正確に1つのメッセージを保持すると仮定できます。
このようなメッセージごとに、messageイベントが生成されます。
let eventSource = new EventSource("/events/subscribe");
eventSource.onmessage = function(event) {
console.log("New message", event.data);
// will log 3 times for the data stream above
};
// or eventSource.addEventListener('message', ...)
クロスオリジンリクエスト
EventSourceは、fetchやその他のネットワークメソッドと同様に、クロスオリジンリクエストをサポートしています。任意のURLを使用できます。
let source = new EventSource("https://another-site.com/events");
リモートサーバーはOriginヘッダーを取得し、続行するにはAccess-Control-Allow-Originで応答する必要があります。
資格情報を渡すには、次の追加オプションwithCredentialsを設定する必要があります。
let source = new EventSource("https://another-site.com/events", {
withCredentials: true
});
クロスオリジンヘッダーの詳細については、Fetch:クロスオリジンリクエストの章を参照してください。
再接続
作成時に、new EventSourceはサーバーに接続し、接続が切断されると再接続します。
これは非常に便利です。気にしなくて済みます。
再接続間には短い遅延があり、デフォルトでは数秒です。
サーバーは、応答でretry:を使用して推奨される遅延(ミリ秒単位)を設定できます。
retry: 15000
data: Hello, I set the reconnection delay to 15 seconds
retry:は、いくつかのデータと一緒に、またはスタンドアロンメッセージとして送られる場合があります。
ブラウザは、再接続する前にそのミリ秒数待つ必要があります。または、ブラウザが(OSから)現在ネットワーク接続がないことを知っている場合など、より長く待つ場合があります。接続が表示されるまで待ってから再試行します。
- サーバーがブラウザに再接続を停止させたい場合は、HTTPステータス204で応答する必要があります。
- ブラウザが接続を閉じたい場合は、
eventSource.close()を呼び出す必要があります。
let eventSource = new EventSource(...);
eventSource.close();
また、応答のContent-Typeが正しくない場合、またはHTTPステータスが301、307、200、204と異なる場合、再接続は行われません。このような場合、「error」イベントが送信され、ブラウザは再接続しません。
接続が最終的に閉じられた場合、「再開」する方法はありません。再度接続したい場合は、新しいEventSourceを作成してください。
メッセージID
ネットワークの問題により接続が切断された場合、どちら側もどのメッセージが受信され、どのメッセージが受信されなかったかを確認できません。
接続を正しく再開するには、各メッセージにidフィールドが必要です。
data: Message 1
id: 1
data: Message 2
id: 2
data: Message 3
data: of two lines
id: 3
id:付きのメッセージを受信すると、ブラウザは
- プロパティ
eventSource.lastEventIdをその値に設定します。 - 再接続時に、その
idをLast-Event-IDヘッダーで送信し、サーバーが後続のメッセージを再送信できるようにします。
data:の後にid:を配置します。ご注意ください:lastEventIdがメッセージ受信後に更新されるように、サーバーによってメッセージdataの下にidが付加されます。
接続状態:readyState
EventSourceオブジェクトにはreadyStateプロパティがあり、3つの値のいずれかを持ちます。
EventSource.CONNECTING = 0; // connecting or reconnecting
EventSource.OPEN = 1; // connected
EventSource.CLOSED = 2; // connection closed
オブジェクトが作成されたとき、または接続が切断されたときは、常にEventSource.CONNECTING(0に等しい)です。
このプロパティをクエリして、EventSourceの状態を確認できます。
イベントタイプ
デフォルトでは、EventSourceオブジェクトは3つのイベントを生成します。
message– 受信したメッセージ。event.dataとして使用できます。open– 接続が開いています。error– 接続を確立できませんでした(例:サーバーがHTTP 500ステータスを返しました)。
サーバーは、イベントの先頭にevent: ...を使用して別のタイプのイベントを指定できます。
例えば
event: join
data: Bob
data: Hello
event: leave
data: Bob
カスタムイベントを処理するには、onmessageではなくaddEventListenerを使用する必要があります。
eventSource.addEventListener('join', event => {
alert(`Joined ${event.data}`);
});
eventSource.addEventListener('message', event => {
alert(`Said: ${event.data}`);
});
eventSource.addEventListener('leave', event => {
alert(`Left ${event.data}`);
});
完全な例
これが、1、2、3、そしてbyeを送信し、接続を切断するサーバーです。
その後、ブラウザは自動的に再接続します。
let http = require('http');
let url = require('url');
let querystring = require('querystring');
let static = require('node-static');
let fileServer = new static.Server('.');
function onDigits(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream; charset=utf-8',
'Cache-Control': 'no-cache'
});
let i = 0;
let timer = setInterval(write, 1000);
write();
function write() {
i++;
if (i == 4) {
res.write('event: bye\ndata: bye-bye\n\n');
clearInterval(timer);
res.end();
return;
}
res.write('data: ' + i + '\n\n');
}
}
function accept(req, res) {
if (req.url == '/digits') {
onDigits(req, res);
return;
}
fileServer.serve(req, res);
}
if (!module.parent) {
http.createServer(accept).listen(8080);
} else {
exports.accept = accept;
}<!DOCTYPE html>
<script>
let eventSource;
function start() { // when "Start" button pressed
if (!window.EventSource) {
// IE or an old browser
alert("The browser doesn't support EventSource.");
return;
}
eventSource = new EventSource('digits');
eventSource.onopen = function(e) {
log("Event: open");
};
eventSource.onerror = function(e) {
log("Event: error");
if (this.readyState == EventSource.CONNECTING) {
log(`Reconnecting (readyState=${this.readyState})...`);
} else {
log("Error has occured.");
}
};
eventSource.addEventListener('bye', function(e) {
log("Event: bye, data: " + e.data);
});
eventSource.onmessage = function(e) {
log("Event: message, data: " + e.data);
};
}
function stop() { // when "Stop" button pressed
eventSource.close();
log("eventSource.close()");
}
function log(msg) {
logElem.innerHTML += msg + "<br>";
document.documentElement.scrollTop = 99999999;
}
</script>
<button onclick="start()">Start</button> Press the "Start" to begin.
<div id="logElem" style="margin: 6px 0"></div>
<button onclick="stop()">Stop</button> "Stop" to finish.概要
EventSourceオブジェクトは、永続的な接続を自動的に確立し、サーバーがその接続を介してメッセージを送信できるようにします。
それは以下のものを提供します。
- 調整可能な
retryタイムアウトによる自動再接続。 - イベントを再開するためのメッセージID。最後に受信した識別子は、再接続時に
Last-Event-IDヘッダーで送信されます。 - 現在の状態は
readyStateプロパティにあります。
これにより、EventSourceはWebSocketの適切な代替手段となります。後者はより低レベルであり、そのような組み込み機能(実装できますが)がありません。
多くの現実世界のアプリケーションでは、EventSourceのパワーで十分です。
すべての最新のブラウザでサポートされています(IEを除く)。
構文は次のとおりです。
let source = new EventSource(url, [credentials]);
第2引数には、{ withCredentials: true }という1つのオプションしかありません。クロスオリジンの資格情報を送信できます。
全体的なクロスオリジンのセキュリティは、fetchやその他のネットワークメソッドと同じです。
EventSourceオブジェクトのプロパティ
readyState- 現在の接続状態:
EventSource.CONNECTING (=0)、EventSource.OPEN (=1)、またはEventSource.CLOSED (=2)のいずれか。 lastEventId- 最後に受信した
id。再接続時にブラウザはそれをLast-Event-IDヘッダーで送信します。
メソッド
close()- 接続を閉じます。
イベント
message- メッセージを受信しました。データは
event.dataにあります。 open- 接続が確立されました。
error- 接続が切断された場合(自動的に再接続されます)や致命的なエラーが発生した場合など、エラーが発生した場合。
readyStateを確認して、再接続が試行されているかどうかを確認できます。
サーバーは、event:でカスタムイベント名を設定できます。このようなイベントは、on<event>ではなくaddEventListenerを使用して処理する必要があります。
サーバーの応答形式
サーバーは、\n\nで区切られたメッセージを送信します。
メッセージには、次のフィールドが含まれる場合があります。
data:– メッセージ本文。複数のdataのシーケンスは、部分間に\nを持つ単一のメッセージとして解釈されます。id:–lastEventIdを更新し、再接続時にLast-Event-IDで送信されます。retry:– ミリ秒単位で再接続の再試行遅延を推奨します。JavaScriptから設定する方法はありません。event:– イベント名。data:の前に付ける必要があります。
メッセージには、任意の順序で1つ以上のフィールドを含めることができますが、id:は通常最後に来ます。
コメント
<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkr、jsbin、codepen…)を使用してください。