WPF/日記12
チャットの通信方式
フキダシの表示も可能になったので、いよいよチャットサーバと通信をして取得したデータを画面に反映させる辺りを実装したいと思います。WEBページでよくみかけるチャットでは、HTTPプロトコルを使って、定期的に画面をリロードするというタイプが多い・・・というかHTTPの特性上そうならざるをえないわけですが・・・サーバと双方向に常時接続を保って即時に会話をやりとりするというタイプも人気があると思います。またP2Pライクにユーザがサーバを介さずに直接通信を行ってくれれば、サーバの構築が必要最低限で済むので楽そうではあります。
ただ、この手の通信に関しては、おそらくXBAPの制限事項が少なからずあると思います。そこら辺の見極めをしないことには、手がつけられないということになります。そしてざっと確認した感じでは、.NET 3.5以降でXBAPで可能になった接続方式は以下のHTTP方式だけのようです。
- BasicHttpBinding
- WebHttpBinding
- WSHttpBinding(トランスポート セキュリティ モードのみ)
NetTcpBindingのようなTCP方式もWSDualHttpBindingのような双方向通信も、部分信頼ではサポートされないということになるようです。というわけで、XBAPでのチャットは定期的にメッセージを取得するタイプが望ましいようです。
このタイプでは、メッセージの更新間隔をどの程度に設定するかでサーバへの負荷が変わってきます。通常の運用で30秒に一回程度定期的にメッセージを更新し、それとは別にメッセージを投げかけたときにもメッセージログを取得するというルールで支障はないかななんて思いますが、運用するまでわからない部分でもあります。更新期間をサーバから指定できる仕組みを作っておけば、高負荷時でのパフォーマンス改善に役立つのでしょうね。
もう一つ、片方向での通信のため、ユーザがシステム上での正式な手続きを踏まずに退室した場合、システムには一切通知がこないケースが想定されます。これは接続ユーザの最新アクセス時間を確認して、5分ぐらいアクセスがなければ自動的にログアウトするという仕組みをチャットサーバ側に持たせればいいでしょうかね。チャット中にブラウザを閉じられる、あるいはページを遷移されるというケースはイベントで処理できるかもしれませんが、やはり、突然通信が途切れたり、マシンがとんでしまうということもありますよね。
private DispatcherTimer reloader = new DispatcherTimer();
reloader.Interval = TimeSpan.FromSeconds(30);
reloader.Tick += new EventHandler(this.reloadSpeech);
reloader.Start();
上記のように30秒インターバルのタイマーを新規に作成します。
void reloadSpeech(object sender, EventArgs e)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += delegate(object sender2, DownloadStringCompletedEventArgs e2)
{
xmlAction(e2.Result);
};
wc.DownloadStringAsync(new Uri("http://soft.candychip.net/xml/xml/ref080502.xml"));
}
30秒毎に上記のメソッドが呼び出されます。WebClient.DownloadStringAsyncは、指定のURLの内容を取得するものです。処理が完了するとWebClient.DownloadStringCompletedの中身が呼び出されます。同様に、文字を入力したときのPOST送信処理もWebClient.UploadStringAsyncに書き換えておきました。これでチャットサーバからのXMLメッセージはxmlActionメソッドに渡されるようになったはずです。
XMLのパース
取得したXMLをXmlDataProviderなんかを使ってコントロールにBinding・・・なんてことができたら、WPFのすごいところが発揮できたと思うのですが、まだ何の設計も済んでいない状況では時期尚早ってことで、とりあえずQueueのコレクションに読み込んだXMLの
Queue<string> messagequeue = new Queue<string>();
void xmlAction(string response)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(response);
XmlNodeList nodes = doc.SelectNodes("/ChatActions/Speech/Message");
foreach (XmlNode node in nodes) {
messagequeue.Enqueue(node.InnerText);
}
}
そして回しているタイマーの中でキューのカウントをチェックしてメッセージが溜まっていたら、一件フキダシを表示するようにしてみます。
if (messagequeue.Count > 0)
{
viewTalkingBox(messagequeue.Dequeue());
}
さて、viewTalkingBoxメソッドに渡された文字列をフキダシの中のTextBlockに入れてやろうとした段階で、入れ方がわからなくなってしまいました。3DオブジェクトのマテリアルにVisualBrushを用いて、フキダシの枠や文字を描こうと思っていたのですが、階層の深いところにあるTextBlockのTextプロパティをコード上で変更する方法が思いつきませんでした。リソースディクショナリからフキダシを呼んでいることが、一層事態の把握を鈍らせているような感じです。まだまだ、勉強が足りてないなぁと思いつつ、次はそこら辺を解決していきたいと思います。