Documentation

You are viewing the documentation for the 2.2.0 release in the 2.2.x series of releases. The latest stable release series is 2.4.x.

§Coment ソケット

§Comet ソケットを作成するためにチャンクレスポンスを利用する

チャンクレスポンス を応用すると、Comet ソケットを作成することができます。 Comet ソケットは、 <script> のみを含むチャンク分割された単なる text/html レスポンスです。それぞれのチャンクに、 web ブラウザによって実行される JavaScript を含んだ <script> タグを書き込みます。これを利用することで、サーバから web ブラウザへ、イベントをリアルタイムに送信することができます: それぞれのメッセージ毎に、JavaScript のコールバック関数を呼び出す <script> タグでイベントをラップして、それをチャンクレスポンスに書き込みます。

それでは、これを確かめるデモを作成してみましょう。まず、ブラウザの console.log 関数を呼び出す <script> タグを生成するような Enumerator を作成します。

def comet = Action {
  val events = Enumerator(
    """<script>console.log('kiki')</script>""",
    """<script>console.log('foo')</script>""",
    """<script>console.log('bar')</script>"""
  )
  Ok.stream(events >>> Enumerator.eof).as(HTML)
}

このアクションを web ブラウザから実行すると、ブラウザのコンソールに3つのイベントログが出力されるでしょう。

Tip: events >>> Enumerator.eofevents.andThen(Enumerator.eof) の別の書き方です。

Enumerator[A] を別の Enumerator[B] へ変換する play.api.libs.iteratee.Enumeratee を利用すると、この例はもっと簡単に書けます。試しに、標準的なメッセージを <script> タグでラップするのに使ってみましょう。

import play.api.templates.Html

// Transform a String message into an Html script tag
val toCometMessage = Enumeratee.map[String] { data =>
  Html("""<script>console.log('""" + data + """')</script>""")
}

def comet = Action {
  val events = Enumerator("kiki", "foo", "bar")
  Ok.stream((events &> toCometMessage) >>> Enumerator.eof)
}

Tip: events >>> Enumerator.eof &> toCometMessageevents.andThen(Enumerator.eof).through(toCometMessage) の別の書き方です。

§play.api.libs.Comet ヘルパーを使う

チャンク分割された comet ストリームを扱うために、上で書いた内容とほぼ同じことを行う Comet ヘルパーを用意しています。

ノート: 実際のところ Comet ヘルパは、ブラウザの互換性のため最初に空のバッファデータを送信したり、メッセージとして String と JSON の両方をサポートするなど、上で書いた内容以上のことを行います。さらに、特定の type class を定義することで、他の型のメッセージをサポートするように拡張することもできます。

これを使って前述の例を書き直してみましょう:

def comet = Action {
  val events = Enumerator("kiki", "foo", "bar")
  Ok.stream((events &> Comet(callback = "console.log")) >>> Enumerator.eof)
}

§Forever iframe テクニック

Comet ソケットを書く標準的なテクニックとして、 iframe 内でチャンク分割された Comet レスポンスを無限にロードし、親フレームを呼び出すコールバック関数を特定するというものがあります:

def comet = Action {
  val events = Enumerator("kiki", "foo", "bar")
  Ok.stream((events &> Comet(callback = "parent.cometMessage")) >>> Enumerator.eof)
}

これを、次のような HTML ページと共に使用します:

<script type="text/javascript">
  var cometMessage = function(event) {
    console.log('Received event: ' + event)
  }
</script>

<iframe src="/comet"></iframe>

Next: WebSocket