none
リモーティングでサーバのイベントに登録 RRS feed

  • 質問

  • こんばんは。

    リモーティングでサーバのイベントに登録しようとしたところ以下のエラーが発生します。

    System.Security.SecurityException : 型 System.DelegateSerializationHolder と
    それから派生した型 (例 System.DelegateSerializationHolder) は、このセキュリティ レベルでは逆シリアル化することが許可されていません。

    どなたか解決方法を御存知の方はいないでしょうか。
    あと、なるべく設定ファイルを使わずにコーディングでやりたいです。
    コーディングだけでは無理といった部分があるのであれば
    設定ファイルでも良いんですが。

    コーディングは以下の通りな感じです。ちゃんと通信は出来てます。


    サーバ登録
    public virtual void CreateSingletonRemoteObject( int port, string objName, Type objType ) {
     CheckInstance();
     _chanelConfig = new System.Collections.Hashtable();
     _chanelConfig.Add( "port", port );
     _chanelConfig.Add( "authenticationMode", objName );
     _chanelConfig.Add( "name", "" );   // 匿名チャネル
     _chanelConfig.Add( "secure", true );

     BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
     provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

     TcpChannel channel = new TcpChannel( _chanelConfig, null, provider );

     // サーバチャネルを登録
     ChannelServices.RegisterChannel( channel, false );

     // リモートオブジェクトに名前を付けインスタンス生成
     RemotingConfiguration.RegisterWellKnownServiceType(
      objType,
      objName,
      WellKnownObjectMode.Singleton
     );
    }


    クライアント登録
    public object CreateRemoteScreenObject(
      string server, int port, string objName, Type objType ) {
     IDictionary chanelConfig =
      new System.Collections.Hashtable();
     chanelConfig.Add( "authenticationMode", objName );
     chanelConfig.Add( "name", "" );   // 匿名チャネル
     chanelConfig.Add( "secure", true );
     BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
     provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
     // クライアントチャネルを登録
     TcpChannel channel = new TcpChannel( chanelConfig, null, provider );
     ChannelServices.RegisterChannel( channel, false );
     // リモートオブジェクトをアクティベート
     object remoteObject = Activator.GetObject(
      objType,
      string.Format( "tcp://{0}:{1}/{2}",
       server, port.ToString(), objName
      )
     );
     return remoteObject;
    }


    クライアント呼び出し
    IOutsideMessageObject obj =
    (IOutsideMessageObject)client.CreateRemoteScreenObject( "localhost", 60002, "OutsideMessageObject", typeof( IOutsideMessageObject ) );

    obj.Receved += new RecevedEventHandler( obj_Receved );

    chanelConfig.Add( "secure", true );
    を false にすると音沙汰がなくなります(T_T
    あと、localhostでやってます。
    2006年9月1日 12:57

回答

  • 多分無理。

    ってか、.NETリモーティングでつなぎに言ったクライアントにサーバから逆につなぎに行かないとダメでしょう。

    シングルトンとかってイメージにはなりませんから。

    #専門家プリーズ(^^;;

    2006年9月2日 14:07

すべての返信

  • ジコレスです。

    テストの仕方が悪かったのかもしれませんが
    ChannelServices.RegisterChannel( channel, true );
    でやったところ、なんとなくその先の話な気がする
    違うエラーが出ることが判明しました。

    System.Runtime.Serialization.SerializationException: アセンブリ 'testBase', Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' が見つかりません。

    とかいうぜんぜん違うクラス(自作クラス)の名前で
    何でそのクラスが出てくるのかよくわかりません。
    とりあえず[Seralizeble]とかつけてみたんですが最終的に

    System.Runtime.Serialization.SerializationException: アセンブリ 'System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' の型 'System.Windows.Forms.Form+ControlCollection' はシリアル化可能として設定されていません。

    とか言われました。
    なんか、当初の予想とまったく反した事態に困惑気味です。
    イベントに紐付けるだけで自クラスはまだしも他のクラスまでを
    シリアライズしないといけない理由がよくわからないです。
    サーバのイベントにクライアントまでの道のり(アドレス、ポート、メソッド)
    とかを設定しているつもりだったのですけども
    そういった単純なことでなくて、ものすごいことでもしているんでしょうか?
    何が起こっているのかを教えていただければ幸いです。

    2006年9月1日 14:46
  • シリアライズとはメモリ上に展開されているクラスをすべて自己復元できる形(バイナリとかね)に保存することです。

    そうすることによって元に戻せるわけです。

    EventクラスまたそのEventが保持しているすべての参照のクラスがシリアライズ可能でなければいけません。

    自分でシリアライズ可能なEventクラスを作って、それをサーバ側でEventを作り上げる形にするほうが簡単でしょう。

    2006年9月1日 14:55
  • 返信ありがとうございます。

    非常に参考になったのですが、ただ、私の知識が乏しいために
    実装イメージがいまいち見えません。

    Eventクラスとはいわゆるデリゲートの事でしょうか?
    多分違う気がしましたがとりあえずトライしてみました。

    今回テストで作った構成
    サーバ配置
    RemoteServer
    ServerObject
    リモーティングとしてリッスンする。
    リモーティングで動くクラス。イベント配信。
    クライアント配置
    RemoteClient
    Client
    リモートオブジェクト作成する。
    イベント登録。受信。
    共通配置
    IRemoteObject ServerObject のインターフェイス + delegate
    (両方から参照したいのでデリゲートを IRemoteObject に配置。)

    >シリアライズ可能なEventクラスを作って
    [Serializable]
    public delegate void MesEventHandler( string mes );

    という事でしょうか?。(多分違います。

    サーバでリッスン。
    イベント用意。(ServerObject内)
    public event MesEventHandler Mes;

    クライアントからリモートオブジェクト作成(_remoteObject)して
    イベント登録
    _remoteObject.Mes += new MesEventHandler( ReceveMes );

    System.Runtime.Serialization.SerializationException: アセンブリ 'Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' が見つかりません。

    ダメだったので
    >それをサーバ側でEventを作り上げる形
    というのを私なりに理解してみたんですが明確に思い当たる節がなく

    >シリアライズ可能なEventクラスを作って
    まさか!(ぉぉ
    MemoryStream ms = new MemoryStream();
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize( ms, new MesEventHandler( ReceveMes ) );


    System.Runtime.Serialization.SerializationException: アセンブリ 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' の型 'System.Runtime.Serialization.SerializationInfo' はシリアル化可能として設定されていません

    撃沈でした。orz

    ちょっとだけ具体的にしていただければ幸いです。(T_T
    2006年9月2日 4:39
  • >シリアル化可能として設定されていません

    ってのは、

    [Serializable]
    public class SerializableEvent : MarshalByRefObject
    {
       public string EventName;
    }

    ってかんじにしてやればいいだけです。

    というかもしかして

    >リモーティングでサーバのイベントに登録しようとしたところ以下のエラーが発生します。

    System.Diagnostics.EventLog クラスのことやなくって、単純にeventに登録ってだけの話?(^^;;;

    2006年9月2日 12:25
  • >System.Diagnostics.EventLog クラスのことやなくって、単純にeventに登録ってだけの話?(^^;;;
    って、ぢつはそうなんですよ!。

    サーバ上にあるevent(サーバ上で動いているシングルトンリモートオブジェクトのevent)に登録したいわけです。
    1、サーバ上に配置する、クライアントからのリモート操作対象(リモートオブジェクト)となるクラスにイベントを定義して
    2、シングルトンで生成待ちうけ。
    3、クライアントはそこめがけてリモート接続。
    4、リモートオブジェクトを通じてのイベント登録→失敗。。。ぁぅ。
    用語の使い方とか非常に不安な限りではありますが。。。

    結局やりたかったのはサーバから、イベント登録クライアントへのマルチキャスト(複数同時配信)ですね。
    イベント使えば楽 だなーと思ったのがそもそも(結果的には)間違っていたんですけども
    なんとなく出来そうな気もして、わからないまま放置したくなかったので。
    もしかして、ものすごく本当は簡単で、なんかどーしようもないところで間違っているのかもしれませんケド。

     中博俊 さんからの引用

    [Serializable]
    public class SerializableEvent : MarshalByRefObject
    {
       public string EventName;
    }

    コレはやっっぱり違うのかな。と思っています。(なんか、勘違いさせてしまったみたいですみません;
    サーバがどのクライアントのどのメソッドをコールしなくてはいけない
    という情報を持っていないといけないはずで、リモートって一方通行ですよね??
    だとすると、サーバ的にはクライアントの情報っていうのは知らないわけで
    文字情報だけでなく、デリゲートでももらわなきゃいけない気がするんですけども。
    ちがうのかな~。う~ん。。。

     伝わったかな。伝わるとイイナ

    2006年9月2日 13:57
  • 多分無理。

    ってか、.NETリモーティングでつなぎに言ったクライアントにサーバから逆につなぎに行かないとダメでしょう。

    シングルトンとかってイメージにはなりませんから。

    #専門家プリーズ(^^;;

    2006年9月2日 14:07
  • ガーン Σ(゜▽゜||| マヂデツカ

    イベント デリゲート リモート

    とか

    リモート処理の例 : デリゲートおよびイベント

    とか怪しそうなのがあるんですけども
    結局そのままコピっても実際エラーがりがり出るんでよね。
    やっぱり昔とは違うんですかね~。
    #いまさらながら2005での開発でした。

    出来ないってことでしたら仕方ないですね=。
    出来ない事がわかったんで、今日はゆっくり寝れそうですw。
    中様ありがとうございましたっ。

    2006年9月2日 14:46
  • 追記
     その後あれこれやっているうちになんとなくわかりました。(←まだやってた(笑
    つまりサーバー内で完結しようとしているって事なんですね。
    ファイルストリームとかクライアントで読めたんで誤解してました。

    イベント発生→クライアントで登録したと思っていたメソッドをリモートオブジェクトはサーバ内で探しに行く→結果アセンブリがないので例外。
    もし、アセンブリがあればサーバ内でインスタンス作られるみたいです。

    インスタンス指定してのイベント登録しても同じ動きでした。
    その辺はなんか面白いなと思うんですが紛らわしいのでやめて欲しいと思いました(笑。

    結局本当に一方通行なんだなぁと感心しました。(おわり


    ps.誤解を恐れずにかいてみます。(弱気)
    デリゲートはネットワーク対応していなくて、登録情報にもアドレスとかポートとかないし、
    だから、登録されたものは全て自身のオブジェクトだと思っていると。

    それで、戻り値とかあればまた違うのかもしれないけど
    今回の場合はイベントをたたくだけなのでサーバ内でさがしにいっちゃたんですね。
    2006年9月6日 1:57
  • で、それを解決する方法がわかったんでいちおうフィードバックしておきます。
    あまりにも前の話なんでアレですが。。。
    あと、上のコメントになんか勘違いが混ざっていることも多々ある事にも気づいてみたり(-_-;


    何かといえばもう一個クライアント用に用意するって事でした。

    RemoteObject clientRecv = new RemoteObject();
    clientRecv.Receved += new RecevedEventHandler( obj_Receved );
    obj.Receved += new RecevedEventHandler( clientRecv.OnReceved );

    命名の整合性がとれなくなってますけど;
    リモートオブジェクトで用意するとサーバー側で発生するんで
    リモートオブジェクトを使用しないインスタンスを生成して
    それとつなぐとうまく行くって事でしょうか。
    サーバ側でもアクティベートするとアクティベートしたリモートオブジェクトを利用してサーバ側からも発信できますね。

    あーそうそうリモートオブジェクトのOnReceved(つまりイベント発生させるメソッド)に[OneWay]属性をつけなくてはいけません。
    と、クライアントのチャネルはポート0(1台1個なら問題ないかも)と
    チャネルの2番目の引数をfalseにしておくことが必要ですね。

    コレ見て参考なる人がいると良いと思いました。(ほんとに終わり。

    2006年10月27日 11:20
  • おお、今時分も同じ問題で苦労しています。

    ただ、moaさんの報告だけだと、いまいち内容がつかめないので、

    全体のソースコードとか、出せませんかねぇ。

    2006年12月3日 5:19
  • こんにちは。moaです。

    説明下手でごめんなさい。(o_ _)o
    というか、今読もうとしたら途中で読む気力がなくなるような文章でした(テヘヘ
    なんで、私が読んでもよくわからないので本当に同じ事をしようとしているのかは自信無いですケド
    いちおーサンプルのっけますね。

    サンプルで作ってうじうじ悩んでいたんでかなりきちゃないソースになってしまったんで
    名前とか直したんで↑のソース名とかとは違うんですけど御容赦ください。

    やりたいこと。
      ■リモートオブジェクトとして用意したイベントを発生させる!
      手順
    1. イベントを持ったリモートオブジェクトを用意します。
    2. リモートオブジェクトをアクティベートします。
    3. アクティベートしたリモートオブジェクトのイベントハンドラに登録します。
    4. アクティベートしたリモートオブジェクトのイベントを発生させます。
    5. イベントハンドラに登録したメソッドが呼ばれていることを確認します。
    リモートオブジェクト
    RemoteObject.cs
    using System;
    using System.Runtime.Remoting.Messaging;
    
    namespace Remote {
      /// <SUMMARY>
      /// メッセージを送信するイベントハンドラを定義します。
      /// </SUMMARY>
      public delegate void SendMessageEventHandler();
    
      /// <SUMMARY>
      /// 大元となるメッセージ送信イベントを発生させる機能を提供します。
      /// </SUMMARY>
      public class RemoteObject : MarshalByRefObject, IRemoteObject {
      /// <SUMMARY>
      /// メッセージイベント
      /// </SUMMARY>
      public event SendMessageEventHandler SendMessage;
    
      /// <SUMMARY>
      /// メッセージイベントを発生させます。
      /// </SUMMARY>
      [OneWay]
      public void OnMessageSend() {
      if ( SendMessage != null ) {
        SendMessage();
       }
      }
     }
    
      /// <SUMMARY>
      /// メッセージ送信イベントをチェインさせる為の機能を提供します。
      /// </SUMMARY>
     public class ChainRemoteObject : MarshalByRefObject, IRemoteObject {
      /// <SUMMARY>
      /// チェインするメッセージイベント
      /// </SUMMARY>
      public event SendMessageEventHandler SendMessage;
    
      /// <SUMMARY>
      /// チェインメッセージを発生させます。
      /// </SUMMARY>
      [OneWay]
      public void OnMessageSend() {
       if ( SendMessage != null ) {
        SendMessage();
       }
      }
     }
    }
    

    IRemoteObject.cs
    using System;
    namespace Remote {
     /// <SUMMARY>
      /// メッセージを送信するインターフェイスを表します。
      /// </SUMMARY>
      public interface IRemoteObject {
      event SendMessageEventHandler SendMessage;
      void OnMessageSend();
     }
    }
    

    クライアント
    RemoteClient.cs
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Collections;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using System.Runtime.Remoting;
    
    namespace Remote {
     class RemoteClient {
      /// <SUMMARY>
      /// クライアント用 RemoteScreenObject を生成します。
      /// </SUMMARY>
      /// <PARAM name="server">リモート接続するサーバ名</PARAM>
      /// <PARAM name="port">使用するポート番号</PARAM>
      /// <PARAM name="objName>対象のオブジェクト名</PARAM>
      /// <PARAM name="objType>対象のオブジェクトタイプ</PARAM>
      /// <RETURNS>アクティベートされたリモートオブジェクト</RETURNS>
      public object CreateRemoteScreenObject(
       string server, int port, string objName, Type objType ) {
    
       Hashtable chanelConfig =
        new System.Collections.Hashtable();
       chanelConfig.Add( "authenticationMode", objName );
       chanelConfig.Add( "name", "" );   // 匿名チャネル
       chanelConfig.Add( "secure", true );
    
       // クライアントチャネルを登録
       //TcpClientChannel channel = new TcpClientChannel( chanelConfig, null );
       TcpServerChannel channel = new TcpServerChannel( "", 0 );
    
       ChannelServices.RegisterChannel( channel, false );
    
       // リモートオブジェクトをアクティベート
       object remoteObject = Activator.GetObject(
        objType,
        string.Format( "tcp://{0}:{1}/{2}",
         server, port.ToString(), objName
        )
       );
       return remoteObject;
      }
     }
    }
    

    Program.cs
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Remote {
     class Program {
      static void Main( string[] args ) {
       Console.WriteLine("client start");
       Console.WriteLine( AppDomain.CurrentDomain.SetupInformation.ApplicationName.ToString() );
       Console.WriteLine( "regist start, plz enter" );
    
       RemoteClient client = new RemoteClient();
       IRemoteObject serverRemoteObject = client.CreateRemoteScreenObject(
         "localhost", 12345, "remortobj", typeof( IRemoteObject ) ) as IRemoteObject;
    
       RemoteObject clientRemoteObject = new RemoteObject();
    
       clientRemoteObject.SendMessage
         += new SendMessageEventHandler( cr_MesSend ); //たぶんクライアントでうごいてる
       serverRemoteObject.SendMessage
         += new SendMessageEventHandler( clientRemoteObject.OnMessageSend ); //たぶんサーバでうごいてる
       //serverRemoteObject.MesSend
       //   += new MesSendEventHandler( cr_MesSend );<-これだけだとサーバ内で発生
    
    
       Console.WriteLine( "regist MesSend success" );
       Console.ReadKey();
       serverRemoteObject.OnMessageSend();
       Console.ReadKey();
      }
    
       static void cr_MesSend() {
       //Console.WriteLine( AppDomain.CurrentDomain.SetupInformation.ApplicationName.ToString() );
       Console.WriteLine( "クライアント待ちうけイベントだよっ!" );
      }
     }
    }
    

    サーバ
    RemortServer.cs
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Collections;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using System.Runtime.Remoting;
    
    namespace Remote {
     public class RemortServer {
      public RemortServer() {
      }
      public void CreateSingletonRemoteObject( int port, string objName, Type objType ) {
       Hashtable _chanelConfig = new Hashtable();
       _chanelConfig.Add( "port", port );
       _chanelConfig.Add( "name", "" );   // 匿名チャネル
       _chanelConfig.Add( "secure", false );
    
       BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
       provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
    
       TcpServerChannel channel = new TcpServerChannel( _chanelConfig, provider );
    
       // サーバチャネルを登録
       ChannelServices.RegisterChannel( channel, false );
    
       // リモートオブジェクトに名前を付けインスタンス生成
       RemotingConfiguration.RegisterWellKnownServiceType(
        typeof( RemoteObject ),
        objName,
        WellKnownObjectMode.Singleton
       );
    
    /*
    ここでアクティベートしたオブジェクトを使用すれば
    接続されたクライアントに対してメッセージを送信できる。
    配送信可能なオブジェクトとして使える!
    サーバからのメッセージ送信だよっ!
       // リモートオブジェクトをアクティベート
       IRemoteObject remoteObject = Activator.GetObject(
        objType,
        string.Format( "tcp://{0}:{1}/{2}",
         "localhost", port.ToString(), objName
        )
       ) as IRemoteObject;
       Console.ReadLine();
       remoteObject.OnMesSend();
    */
    
      }
     }
    }
    
    

    Program.cs
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Remote {
     class Program {
      static void Main( string[] args ) {
       Console.WriteLine( "server start" );
       Console.WriteLine( AppDomain.CurrentDomain.SetupInformation.ApplicationName.ToString() );
       //RemortDelegateServerTest sever = new RemortDelegateServerTest();
       RemortServer sever = new RemortServer();
       sever.CreateSingletonRemoteObject( 12345, "remortobj", typeof( RemoteObject ) );
       Console.WriteLine( "standby ok" );
       Console.ReadKey();
      }
     }
    }
    

    RemoteObject 以外はコンソールアプリです。
    参照は。。。まぁ適当に(ぉぃ
    文字列に色をつけたかったとか、数字にも色をつけたかったとかetc.etc...ありますがちかれました。
    でぁでぁ、頑張ってくださいね。ノシ

    #インデントくずれてる気がする。。。
    #なんか思いっきり文字化けした(涙。別にメモとっておいてよかったです;
    2006年12月17日 4:05