Server-Sent Events を使用する Web アプリケーションの開発は、とても簡単です。Web アプリケーションへイベントを送り込む、わずかなコードがサーバ上で必要になりますが、Web アプリケーション側は他の種類のイベントを扱うものとほぼ同じ動作になります。
サーバからイベントを受信する
Server-Sent Event API は EventSource
インターフェイスに含まれています。イベントを受け取るためにサーバへの接続を開始するには、イベントを生成するスクリプトの URI を指定する、新たな EventSource
オブジェクトを作成します。例えば:
var evtSource = new EventSource("ssedemo.php");
イベントを生成するスクリプトが別のドメインに存在する場合は、URI とオプションディクショナリーの両方を指定する新たな EventSource オブジェクトを作成します。クライアントスクリプトが example.com にある場合の例:
var evtSource = new EventSource("//api.example.com/ssedemo.php", { withCredentials: true } );
注記: EventSource をサポートしていないブラウザがあります。ブラウザ実装状況を確認してください。
インスタンスを生成したら、メッセージの受け取りを始めることができます:
evtSource.onmessage = function(e) { var newElement = document.createElement("li"); newElement.innerHTML = "message: " + e.data; eventList.appendChild(newElement); }
このコードは入力メッセージ (すなわち event
フィールドを持たない、サーバからの通知) を受信して、メッセージのテキストをドキュメントの HTML にあるリストへ追加します。
addEventListener()
を使用してイベントを待ち受けることもできます:
evtSource.addEventListener("ping", function(e) { var newElement = document.createElement("li"); var obj = JSON.parse(e.data); newElement.innerHTML = "ping at " + obj.time; eventList.appendChild(newElement); }, false);
前のコードと似ていますが、event
フィールドに "ping" が設定されたメッセージがサーバから送られたときに、自動的に呼び出されることが異なります。こちらは data
フィールドの JSON をパースして、情報を出力します。
サーバからイベントを送信する
イベントを送信するサーバサイドのスクリプトでは、MIME タイプ text/event-stream での応答が必要です。各々の通知は、2 つの改行で終わるテキストのブロックとして送信されます。イベントストリームの形式について、詳しくは Event stream format をご覧ください。
私たちが使用している PHP のコード例を以下に示します:
date_default_timezone_set("America/New_York"); header("Content-Type: text/event-stream\n\n"); $counter = rand(1, 10); while (1) { // "ping" イベントを毎秒送信 echo "event: ping\n"; $curDate = date(DATE_ISO8601); echo 'data: {"time": "' . $curDate . '"}'; echo "\n\n"; // シンプルなメッセージをランダムな間隔で送信 $counter--; if (!$counter) { echo 'data: This is a message at time ' . $curDate . "\n\n"; $counter = rand(1, 10); } ob_end_flush(); flush(); sleep(1); }
このコードは、イベントタイプが "ping" のイベントを毎秒生成します。各々のイベントのデータは、イベントが生成された時刻の ISO 8601 形式タイムスタンプを含む JSON オブジェクトです。またランダムな間隔で、シンプルなメッセージ (イベントタイプなし) を送信します。
エラーハンドリング
問題が発生した場合 (ネットワークのタイムアウトやアクセスコントロールに関する問題など) は、エラーイベントを生成します。EventSource で onerror
コールバックを実装すると、エラーに対してプログラムで対処できます:
evtSource.onerror = function(e) { alert("EventSource failed."); };
Firefox 22 では、エラーイベントの種類を見分ける方法はありません。
イベントストリームを閉じる
デフォルトではクライアントとサーバの間のコネクションを閉じると、コネクションがリセットされます。コネクションは .close()
メソッドで終了します。
evtSource.close();
イベントストリームの形式
イベントストリームはテキストデータのシンプルなストリームであり、UTF-8 を用いてエンコードされなければなりません。イベントストリーム内のメッセージは、2 つの改行文字で区切られます。行の先頭にあるコロンは本質的にコメントを表し、無視されます。
各々のメッセージは、フィールドを一覧化した 1 つ以上のテキスト行で構成されます。各々のフィールドは「フィールド名、その次にコロン、さらにその後にフィールドの値であるテキストデータ」で表されます。
フィールド
以下のフィールド名が仕様書で定義されています:
event
- イベントのタイプです。これが指定されている場合、イベントはブラウザ内で、イベント名に応じたイベントリスナへ送られます。Web サイトのソースコードでは名前付きイベントを受け取るために、
addEventListener()
を使用します。メッセージでイベント名が指定されていない場合は、onmessage
ハンドラが呼び出されます。 data
- メッセージのデータフィールドです。EventSource が
data:
で始まる、複数の連続した行を受け取ったときは、それらを連結して各項目の間に改行文字を挿入します。終端の改行は取り除かれます。 id
EventSource
オブジェクトの last event ID の値に設定する、イベント ID です。retry
- イベントの送信を試みるときに使用する reconnection time です。[What code handles this?] これは整数値であることが必要で、reconnection time をミリ秒単位で指定します。整数値ではない値が指定されると、このフィールドは無視されます。
他のフィールド名は、すべて無視されます。
例
データのみのメッセージ
以下の例では、3 つのメッセージが送信されています。最初のメッセージはコロン文字から始まっているため、コメントです。前述したように、コメントはメッセージが定期的に送信されない可能性がある場合のキープアライブとして有用です。
2 番目のメッセージは、値が "some text" である data フィールドを持っています。3 番目のメッセージは、値が "another message\nwith two lines" である data フィールドを持っています。値に改行文字があることに注意してください。
: this is a test stream data: some text data: another message data: with two lines
名前付きイベント
こちらの例では、名前付きイベントをいくつか送信しています。それぞれのイベントは event
フィールドで指定されたイベント名を持っており、またクライアントでの処理に必要なデータを含む、適切な JSON 文字列を値に持つ data
フィールドもあります。もちろん、data
フィールドは任意の文字列データを持つことができます。JSON である必要はありません。
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."}
組み合わせ
名前なしのメッセージだけ、または名前付きイベントだけを使用しなければならないことはありません。これらを 1 つのイベントストリーム内で混ぜ合わせることができます。
event: userconnect data: {"username": "bobby", "time": "02:33:48"} data: Here's a system message of some kind that will get used data: to accomplish some task. event: usermessage data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
ブラウザ実装状況
機能 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
EventSource のサポート | 9 | 6.0 (6.0) | 未サポート | 11 | 5 |
機能 | Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|
EventSource のサポート | 42 | ? | ? | 11.1 | 4 |