しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET MVC で Server-Sent Events を試す

最近、サーバサイドでの対応が必要な HTML5 関連技術について調べていました。しばやんです。

WebSocket は WCF WebSockets 使ったら楽勝!でしたが、HTML5 には Server-Sent Events という Comet を簡単に使えるようにした機能が用意されています。これは HTTP 上で動作するので、わざわざ WCF 周りを使うは必要なくて MVC だけで完結することが出来ます。

とりあえず Server-Sent Events の最新仕様です。

http://www.w3.org/TR/eventsource/

大雑把に説明すると以下のようなデータをクライアント側に返すだけです。ただし、コネクションは張りっぱなしになるので、サーバリソース周りは Comet の時と同様に気を付けないといけません。

id: 1
data: hauhau
retry: 1000

id はこのイベントの ID、data は送信するデータ、retry は再接続する際の待ち時間 (ms) になっています。ちなみに最後に送信された id の値は、クライアントからのリクエストに Last-Event-ID というヘッダで付加されてくるので、どのデータまで送信したか分かるわけですね。

最後に忘れやすい点として、このデータを送信する際には Content-Type を text/event-stream にする必要があることです。適切に設定されていない場合には、データは送信されているのにイベントが呼ばれないことになります。

今回、Server-Sent Events のサーバ側を MVC 4 の非同期コントローラを利用して実装しました。コードは以下の通りです。

public async Task<ActionResult> Event()
{
    // 今回は非同期で 5 秒待つ
    await Task.Delay(5000);

    // クライアントに送信するデータを作成
    var data = "id: 1\n";
    data += "data: hauhau\n";
    data += "retry: 1000";

    // text/event-stream として返す
    return Content(data, "text/event-stream");
}

本来ならば Task.Delay の部分をイベント完了待ちとかにする必要がありますが、プロトコルがシンプルなのでデータを返す部分は MVC の Content メソッドで問題ありません。

そしてクライアント側は単純にメッセージを受け取って、リスト表示するだけとなっています。

<script type="text/javascript">
    $(function () {
        var source = new EventSource("/Home/Event");

        source.onmessage = function (e) {
            $("<li />").text(e.data).appendTo("#list");
        };
    });
</script>

<ul id="list"></ul>

Server-Sent Events をクライアント側から使うには EventSource オブジェクトを使います。初期化時に URL を指定してイベントを登録すると、後はサーバ側の応答待ちとなり、応答がある度に onmessage に登録した関数が呼び出されます。

実際に動かすと以下のようになります。ブラウザの対応状況としては IE 以外だと大体使えるようです。

レスポンスが返ってくるまでに 5 秒かかっていますね。コネクションが自動的に張り直されるので、継続的にイベントを受け取ることが出来ています。

別に ASP.NET だから HTML5 関連技術が使えないということはないので、こういった記事が増えるのを期待したいですね。