しばやん雑記

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

WCF WebSockets で実現するリアルタイム Web

最近は Node.js と socket.io で WebSocket を使うことが多いようですね。Windows Azure でも動作する node.exe がリリースされたり、Microsoft もオープンソース寄りになってきたのかと思います。

しかし!わざわざ .NET 開発者が Node.js を使う必要があるのか?と言われると、私は必要ないと考えています。

何故なら .NET のインフラストラクチャには非同期機能はあらかじめ用意されており、WebSocket も HTML5Labs でのドラフト版という形ですが WCF WebSockets が公開されています。ちなみに WebSocket は IE10 で対応される予定ですし、既に Silverlight を使うことで IE9 などでも使えるようになっています。

http://html5labs.interoperabilitybridges.com/prototypes/websockets/websockets/info

WCF WebSockets をお勧めする理由、それはやはり使い慣れた言語である C# と高機能な統合開発環境である Visual Studio で開発が出来ることです。そして .NET 上で構築されているので、新しい言語や考え方を学ぶ必要がないところも重要ですね。

ぐだぐだ語るのはこれぐらいにして、実際に WebSocket を使ったサービスを作ってみることにしましょう。とりあえず上記のページから WCF WebSockets をインストールしておいてください。

今回は定番のチャットアプリケーションを作ってみたいと思います。まずは空の ASP.NET アプリケーションを作成して、System.SerivceModel.dll と先程インストールした WCF WebSockets の中でもサーバサイドを実装している Microsoft.ServiceModel.WebSockets.dll を参照に加えておいてください。

それでは WebSocket のサービスを提供するクラスを作ります。WCF WebSockets では WebSocket を実装するための基本クラスとして WebSocketsSerivce が用意されているので、こいつを継承して作るようになっています。

言葉ばかりじゃわかりにくいのでコードを見て理解してください。

using System;
using System.Collections.Generic;
using System.ServiceModel;

using Microsoft.ServiceModel.WebSockets;

namespace WebSocketsDemo
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class WebSocketsDemo : WebSocketsService
    {
        private static readonly List<WebSocketsDemo> _sessions = new List<WebSocketsDemo>();

        // WebSocket セッションが開始された時に呼ばれる
        public override void OnOpen()
        {
            // セッションをリストに追加
            _sessions.Add(this);
        }

        // WebSocket セッションが閉じられた時に呼ばれる
        protected override void OnClose(object sender, EventArgs e)
        {
            // セッションをリストから削除
            _sessions.Remove(this);
        }

        // クライアントからのメッセージを受信した時に呼ばれる
        public override void OnMessage(string value)
        {
            // 登録されているセッションに対してメッセージを送信
            foreach (var session in _sessions)
            {
                session.SendMessage(value);
            }
        }
    }
}

大した中身でもないのでほぼ説明はいらない気がしますが、WebSocketsService は OnOpen/Close/Message などのオーバーライド可能なメソッドが用意されているので、必要な部分だけオーバーライドするだけで WebSocket の機能を使うことが出来ます。今回はサンプルなので同期処理などを省いていますが、非常に少ないコードだけでチャットアプリケーションのサーバサイド実装が可能なことが分かっていただけると思います。イベントドリブンという WinForm の頃から使われているパターンなので、非常に理解もしやすいかと思います。

そしてサーバからクライアントへメッセージを送信するには SendMessage メソッドを使います。このメソッドを呼び出すと、クライアントへ即座にメッセージが送信されます。

これでサービスクラスの実装は終わりましたので、実際にサービスとして公開するための設定を行います。WebSocket サービスとして起動させる必要があるので、今回は Global.asax の Application_Start メソッドに起動するためのコードを追加しましょう。

protected void Application_Start(object sender, EventArgs e)
{
    // サービスが公開されるアドレス、ポート番号
    var uri = new Uri("ws://localhost:1024/chat");

    // URI とサービスクラスを渡して、ホストを初期化
    var ws = new WebSocketsHost<WebSocketsDemo>(uri);

    // WebSocket のエンドポイントを追加して、接続を待機
    ws.AddWebSocketsEndpoint();
    ws.Open();
}

コメントであらかた書いてありますが、まずは公開するアドレスとポート番号を決める必要があります。当然ながら使用中のポート番号と同じものを指定するとエラーとなり起動できません。そして WebSocketsHost クラスを作成したら AddWebSocketsEndpoint メソッドと Open メソッドを呼び出して完了です。

これだけでサーバ側の実装はすべて完了しました。非常にスマートに書くことが出来ましたね。

それでは次はクライアント側の準備をしていきましょう。

現時点で WebSocket に対応しているブラウザは IE 以外という状況ですが、前述したように WCF WebSockets には Silverlight での実装が用意されているので、適切なファイルを読み込むことで問題なく使えるようになっています。

WebSocket も非常にシンプルな API となっているので、さっさとコードを出して説明したいと思います。

// URI を指定して WebSocket オブジェクトを作成(WCF WebSockets の場合は WebSocketDraft, Firefox の場合は MozWebSocket)
var ws = new WebSocket("ws://localhost:1024/chat");

// サーバからメッセージを受け取った時の関数を登録
ws.onmessage = function (e) {
    // e.data に送信されたテキストが入っている
    alert(e.data);
};

// サーバへメッセージを送信
ws.send("はうはう");

はい、最小限のコードですがこんなにもシンプルです。そして WCF WebSockets の WebSocketsService クラスはこの API に合わせて実装されていることも分かりますね。

たったこれだけのコードでサーバとの双方向通信が行えます。コード量が非常に少ないので、動作確認は Firefox 上の Firebug で行いました。

send メソッドで指定した文字列が alert で表示されていることが確認できました。そして、別のブラウザから送信した場合でも alert が表示されることも確認できました。

通常の WebSocket を使ってみた系のブログエントリの場合はここで終わってしまうんですが、今回は WCF WebSockets なのでもうちょっと続きます。流石は Microsoft というべきか、Silverlight での WebSocket 実装があるならば、デスクトップアプリケーション用の WebSocket 実装もちゃんと用意されています!!

サーバサイドを実装した時に使った Microsoft.ServiceModel.WebSockets.dll と同じディレクトリにある Microsoft.ServiceModel.WebSockets.DesktopClient.dll がそれです。それでは実際にコンソールアプリケーションを作成して動作を確認してみましょう。

基本的にはブラウザに実装されている WebSocket オブジェクトと API はほぼ同じなので、特に迷わずに使うことが出来ると思います。それではソースコードです。

static void Main(string[] args)
{
    // WebSocket を初期化
    var ws = new WebSocket("ws://localhost:1024/chat");

    // データを受信した時のイベントハンドラを登録
    ws.OnData += (sender, e) =>
    {
        // 受信した文字列をコンソールに表示
        Console.WriteLine("received: " + e.TextData);
    };

    // 接続開始
    ws.Open();

    while (true)
    {
        // 入力された文字列をサーバへ送信する
        var line = Console.ReadLine();

        ws.SendMessage(line);
    }
}

イベントの名前が違ったり、Open メソッドの呼び出しが必要だったりしますが、それ以外はほぼブラウザと同じような感じですね。それではブラウザとコンソールアプリケーションという混在な環境で通信を行ってみます。

Firefox から送信された文字列がコンソールに表示されていること、コンソールに入力された文字列が Firefox のコンソールに表示されていることが確認できました。ブラウザ以外からの利用がこんなにも簡単に出来ることは、もはや .NET 開発者の特権といってもいいでしょう。

今回は WCF WebSockets があまりにも素晴らしかったので勢いでブログにまとめてみました。しかし、まだ WebSocket 自体の仕様が固まっていない状態なので注意が必要ですが、.NET からでも簡単に WebSocket が利用できることがお分かり頂けたかと思います。

これからも HTML5 関連技術に注目していきたいと思います!