Server-Sent Events の利用

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

ドキュメントのタグと貢献者

 このページの貢献者: yyss
 最終更新者: yyss,