Silverlight で WebSocket を利用する
既に id:shiba-yan がTIPS id:shiba-yan:20111101:1320075209 を紹介している WebSocket だが、Silverlight からも利用できることをご存じだろうか。現在は HTML5Labs で試験的に公開されているのみだが、当該ランタイムをインストールすることで IE9 や Silverlight からも WebSocket を利用することが可能となる。
WebSocket 自体の利用方法については、 以下の記事が参考になるが、Silverlight から WebSocket を利用する方法については言及していない。
- 連載:人気順に説明する初めてのHTML5開発 双方向通信を実現! WebSocketを使いこなそう
今回は、 Silverlight から WebSocket を利用する方法を紹介する。以下のように、Silverlight とブラウザ間で相互に PUSH 通信を行うことが可能となる。
ランタイムのインストール
以下のサイトから wcfwebsockets.msi を取得し、インストーラに従ってランタイムをインストールする。
- HTML5Labs Websocket
インストールが完了すると「C:\Program Files (x86)\Microsoft SDKs\WCF WebSockets\11.06.22\bin」にDLLが配置される。以下の画像を参考にして頂きたい。
サーバ側の実装
まず、ASP.NETのプロジェクトに対し、以下の2アセンブリについての参照を追加する。
- Microsoft.ServiceModel.WebSockets
- System.ServiceModel
次に、id:shiba-yan:20111101:1320075209 の実装例を参考にして、ASP.NET側に Global.asax を追加して以下を記述する。
- Global.asax
using System; using System.Collections.Generic; using Microsoft.ServiceModel.WebSockets; using System.ServiceModel; namespace SLWebSocket.Web { public class Global : System.Web.HttpApplication { WebSocketsHost<WebSocketsDemo.WebSocketsDemo> ws; protected void Application_Start(object sender, EventArgs e) { // サービスが公開されるアドレス、ポート番号 var uri = new Uri("ws://localhost:4503/chat"); // URI とサービスクラスを渡して、ホストを初期化 ws = new WebSocketsHost<WebSocketsDemo.WebSocketsDemo>(uri); // WebSocket のエンドポイントを追加して、接続を待機 ws.AddWebSocketsEndpoint(); ws.Open(); } protected void Application_End(object sender, EventArgs e) { ws.Close(); } } } 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); } } } }
ここで、利用するポートを 4503 にしている点に注意してほしい。これは、WebSocketのプロトタイプ実装では4502-4534 ポートしか利用できないのが原因だ。実装の際には留意が必要となるので、本件以外にもWebSocketプロトタイプ実装の制限や利用手順等を記述した、以下のサイトを参照して欲しい。
- PROTOTYPE IMPLEMENTATION OF THE PROPOSED WEBSOCKET 09 DRAFT SPECIFICATION
クライアント側の実装
ASP.NETプロジェクト同様、作成したSilverlightプロジェクトに対し、まず次のアセンブリを追加する。
- Microsoft.ServiceModel.WebSockets.Silverlight
次に、以下のソースを追記する。特に注意点は無いので、ソースコードを参照して頂きたい。
- MainPage.xaml
<UserControl x:Class="SLWebSocket.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <Button Content="メッセージ送信" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="button1" VerticalAlignment="Top" Width="95" Click="button1_Click" /> <ListBox Height="247" HorizontalAlignment="Left" Margin="12,41,0,0" Name="listBox1" VerticalAlignment="Top" Width="376" /> <TextBox Height="24" HorizontalAlignment="Left" Margin="113,11,0,0" Name="textBox1" VerticalAlignment="Top" Width="275" Text="メッセージ♪" /> </Grid> </UserControl>
- MainPage.xaml.cs
using System; using System.Windows; using System.Windows.Controls; using System.ServiceModel.WebSockets; namespace SLWebSocket { public partial class MainPage : UserControl { WebSocket ws = null; public MainPage() { InitializeComponent(); // サービス側のURIを指定してインスタンスを作成 ws = new WebSocket("ws://localhost:4503/chat"); // イベントを登録してソケットをオープン ws.OnOpen += new EventHandler<EventArgs>(ws_OnOpen); ws.OnData += new EventHandler<WebSocketEventArgs>(ws_OnData); ws.Open(); } void ws_OnData(object sender, WebSocketEventArgs e) { listBox1.Items.Insert(0, e.TextData); } void ws_OnOpen(object sender, EventArgs e) { ws.SendMessage("Silverlight Appのソケットオープン" + DateTime.Now); } private void button1_Click(object sender, RoutedEventArgs e) { if (ws.ReadyState != WebSocketState.Closed) { ws.SendMessage(textBox1.Text + " " + DateTime.Now); } else { listBox1.Items.Insert(0, "ソケットが閉じてます"); } } } }
clientaccesspolicy.xml の配置
サーバ側実装とクライアント側実装は完了したが、このままでは Silverlightからの WebSocketを利用した疎通ができため、 clientacesspolicy.xml を配置する必要がある。
- ネットワーク セキュリティのアクセス制限 (Silverlight) http://msdn.microsoft.com/ja-jp/library/cc645032.aspx
- ドメインの境界を越えてサービスを利用できるようにする http://msdn.microsoft.com/ja-jp/library/cc197955.aspx
配置場所に注意が必要であり、ローカルの開発サーバで実行する場合、ローカルIISのルートに以下の clientacesspolicy.xml を配置する必要がある*1。
- clientacesspolicy.xml
<?xml version="1.0" encoding="utf-8" ?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*" /> </allow-from> <grant-to> <resource path="/" include-subpaths="true" /> <socket-resource port="4502-4530" protocol="tcp" /> </grant-to> </policy> </cross-domain-access> </access-policy>
Windows 7 の環境での配置例については、以下の画像を参考にして頂きたい。
上記の完了後、サンプルを実行すると無事疎通が行えるはずだ。