.NETでプッシュ型通信

SilverlightWindows Azure の組み合わせで何かできないかなぁと思って、この土日はその辺りのことをちらほら調べてた。

WCF / WPF アプリ

Windowsレンタルサーバーに WCF サービスおいて、クライアントも普通の .NET アプリ(Silverlight とかでなく)なら、netTcpBinding とか wsDualHttpBinding の双方向サービスおけば済む話なんですが。

でも、双方向サービスって full-trusted な環境でないと使えないから、Windows Azure ではホストできないのね・・・。(Windows Azure は partial-trusted で動いてるらしい。Azure 側からよそ様にデータ送りつけるってのができない。)

あと、クライアント側も、Xbap は partial-trusted なので無理臭い。

Silverlight その1(Socket 直接利用)

Silverlight だと、basicHttpBinding しか使えなくて困る。

自前サーバー持ってたり専用ホスティングサーバー借りてるなら、Socket を直接使ってプッシュ型通信する方法はあるんですけども。

この方法は自分でも試したみたことあるけども、TcpListener とか触ったことのある人なら割と簡単にできると思う。サーバー側で TcpListener 直接使えるならかなりお勧め。

ちなみに、通常の .NET Framework と Silverlight 版では Socket の仕様が違うので注意(WPFSilverlight でコード使いまわしたいときとかに)。

Silverlight その2(System.ServiceModel.PollingDuplex.dll)

一応、Silverlight でも双方向通信するためのライブラリはあるみたい。

どういうものかというと、

  • Silverlight SDK についてきてる
  • Silverlight 側から定期的にサーバーにポーリングかけることで双方向通信っぽいこと実現してる

というのなんですけども。

ちょっと頑張ろうとしてみたけども、使うの結構大変そう。

WPF アプリの「Visual Studio のサービス参照機能でひな形コード作ってもらって簡単プログラミング」みたいなノリで使えるかと期待して触ると絶望する。

普通に basicHttpBinding 使って、自前でポーリングの仕組み書く方が楽かもしれないなぁ・・・

おまけ

ActiveWeb 借りてるんですけども、最初 WCF サービスが動かなくて困った。

で、ちょっとググったら解決方向発見: 2008-09-21

ほんと、レンタルサーバー上でのデバッグってどうするのがいいんでしょうねぇ。

ちゃんと調べれば、Parallels Plesk とかのコントロールパネル上から見れるログを残せる気もするんですけども。

とりあえず、面倒だったので、表示したいログメッセージを詰めた例外を投げて、エラー画面を通してログを見ることに。

public override ServiceHostBase CreateServiceHost(
    string constructorString, Uri[] baseAddresses)
{
    var sb = new System.Text.StringBuilder();
    foreach (var a in baseAddresses)
    {
        sb.AppendLine(a.AbsoluteUri);
    }
    throw new InvalidOperationException(sb.ToString());
}

その結果、ActiveWeb の場合、ufcpp.jp と www.ufcpp.jp の両方を使ってサイトにアクセスできるのがエラーの原因っぽかった。

baseAddresses に http://ufcpp.jp/ServiceName.svchttp://www.ufcpp.jp/ServiceName.svc の2つが渡されるけども、結局この2つって同じアドレスを指すんで、2度同じエンドポイントを作ろうとして失敗するみたい。

なので、やっぱり適当だけど、以下のような方法でエラー回避できそう。

public class HogeServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        ServiceHost result = base.CreateServiceHost(serviceType,
            baseAddresses.Take(1).ToArray();
        return result;
    }
}

ほんとは、URI が同じアドレス指してるかどうかを判定して、重複を削除するようなコード書くのが正しい気がする。LINQ を使えば割と簡単に書けそうかも。