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…)を使用してください。