添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
豪气的双杠  ·  sv & uvm ...·  1 月前    · 
瘦瘦的企鹅  ·  常见问题·  6 月前    · 
豁达的地瓜  ·  Android gridview ...·  1 年前    · 

「クライアント」は、アクションを起こす・投げる側
「サーバー」は、アクションを受け取って、何かする側

Microsoft .NET の公式記事(上記リンク)がすごく役に立ったので、そのソースをベースに書き進めています

それぞれ見出し的に

  • クラス(型)のみ共有
  • クラス(型) および インスタンス共有
  • クラス(型) および インスタンス共有 および 引数を送る
  • の3つを書いています

    クラスが共有できるらしい。書いてみる

    1.サーバー側処理

    // IPCチャンネルをサーバー側として作成&登録 // これにより、自身はサーバー "ipc://remote" としてアクセスを受け付ける IpcServerChannel serverChannel = new IpcServerChannel("remote"); ChannelServices.RegisterChannel(serverChannel); // Counter という型、クラスを「ウチのこれ使えますよ~」とアプリの外側に周知させる // これによりクライアント(相手)は "ipc://remote/counter" にアクセスして Counterクラ スを受け取れるようになる RemotingConfiguration.RegisterWellKnownServiceType( typeof(Counter), "counter", ellKnownObjectMode.Singleton ); // 呼び出し待ち状態となる Console.WriteLine("Listening on {0}", serverChannel.GetChannelUri());

    .Counterクラス(型)

    同じくサーバ側で用意

    public class Counter : MarshalByRefObject {
      private int count = 0;
      public int Count { get {
        return(count++);
    

    この型は「ウチのこれ使えますよ~」(※ソース内コメント参照)と周知処理させていたものです。 自前で好きなものを作りましょう。
    なお、この型は「名前空間の外」に書いておきましょう
    そうしないと、

    型 ApplicationName.Counter, ApplicationName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null を読み込めません

    のようなエラーを目にすることになるかも...Σ(´ ` )

    共有させるクラスは MarshalByRefObject を継承して作る

    今しがたサンプルとして用意したCounterクラスは MarshalByRefObject というクラスを継承してこさえています。

    MarshalByRefObject の簡単な説明としては

    リモート処理をサポートするアプリケーションで、 アプリケーションのドメインの境界を越えてオブジェクトにアクセスできるようにします。

    との事。(なるほど分からん)
    MarshalByRefObject クラスの力を借りるだけで面倒くさそうな処理がいとも簡単に...すごいですね。

    さて、次はクライアント側のソースです

    2.クライアント側処理

    先程のサーバー側では IpcServerChannel を用いましたが、
    クライアント側では IpcClientChannel クラスを用いています。

    // IPCチャンネルをクライアント側として作成&登録 IpcClientChannel clientChannel = new IpcClientChannel(); ChannelServices.RegisterChannel(clientChannel); // 「ウチのこれ使えますよ~」と別のアプリ(サーバー側)が触れ回っていた Counter という 型、クラスを // クライアント側が自分のもとに引っ張り出して、扱えるようにする RemotingConfiguration.RegisterWellKnownClientType( typeof(Counter) , "ipc://remote/ counter" ); // 相手から引っ張り出してきたクラスをインスタンス化 Counter counter = new Counter(); // 実際に自分の所で使ってみる。 Console.WriteLine("This is call number {0}.", counter.Count);

    Counterクラス(型)

    さて、クライアント側でもサーバー側と似たようなクラスをコードに書いてあげます
    ただし、此方は中身がない状態でOKです。 うわべだけ、処理なし。

    //クライアント側
    public class Counter : MarshalByRefObject
      public int Count { get { throw null; } }
    先ほどサーバー側で書いた Counterクラス と比較してみましょう
    //サーバー側
    public class Counter : MarshalByRefObject {
      private int count = 0;
      public int Count { get {
        return(count++);
    

    内部の count フィールドは private (隠匿されている)なので、クライアント側は、使うときにその情報を知る必要はありません。
    Countプロパティがある」という情報のみ知っていればOKな訳です。

    寸劇風に書くと...

    A「ねぇねぇ、サーバー側から Counter っていうクラスが送られてくるらしいよ」
    A「ウチらにそれ使ってくれって上からお達しが来てさ~」
    B「へ~、どういうクラスなの?」
    A「さー、よくわかんないけど、説明書だけ ※1 渡された。読んでみて」
    B「どれどれ…えっと」
    B『CounterクラスはMarshalByRefObjectを継承して作られているようです』
    A「ふむふむ」
    B『int型のCountプロパティがあるので、そこから数を受け取ってください』
    B『実際は中にprivateフィールドとかあるんですが、貴方達がそれを知る必要はありません』
    B「...だってさ」
    A「なるほどね~、使う側は内部処理を気にする必要が無い…か。 わかりみが深~い」

    ※1 の説明書が、クライアント側のソースに記載した「上辺だけのクラス」って訳です。
    「一応、我々も使う立場なんで "定義だけ" は知っておいてね」みたいな意図ですね。

    という訳で、これら2つの処理をそれぞれ組み込んだ、サーバー側アプリ・クライアント側アプリを立ち上げて検証してみます

    クライアント側で、受け取ったCounterクラスをインスタンス化させた後、使ってみます。

    Windowフォームにボタンでも作っておいて、そこで押すたびに
    この処理だけ走るようにしておけば、わかりやすいかも。

    //クライアント側アプリにて、何かのClickイベント内で呼ぶ
      //これを実行(呼び出)した分だけ、内部カウンターが増える
      Console.WriteLine("This is call number {0}.", counter.Count);
    

    こうすると、サーバー側で記載した処理能力を持つCounterクラスを、クライアント側のソースコードで利用することができます。

    クライアント側でボタンを押すたび、0から始まった値が順に増え続けるというものです
    しかし、その値を保持しているのはあくまでCounterクラスをインスタンス化させたクライアント側のみであり、
    ここ(クライアント側)でいくらボタンを押したからといって、
    サーバー側のCounterクラス(をインスタンス化させたもの)と値自体は共有されない事に注意してください

    しかもこの状態ではまだ「サーバーからクラスを受け取って、クライアントで利用しているだけ」に過ぎないので
    双方向通信とはちょっと言いづらいですね。

    そこで、もうちょっとそれっぽくしてみます。
    今度はインスタンスそのものを共有できる方法です

    インスタンスも共有が出来るらしい。書いてみる

    A.サーバー側

    //フィールドにインスタンスを用意
    public Counter counter = new Counter();
    //メイン処理
    public void mainProcess()
      IpcServerChannel serverChannel = new IpcServerChannel("remote");
      ChannelServices.RegisterChannel(serverChannel);
      //処理を差し替え。
      //Counter クラスから作ったインスタンスを「これ使ってね~」と登録。
      //"ipc://remote/counter" で相手方が得られるのは変わらない
      RemotingServices.Marshal(Counter, "counter", typeof(Counter));
      // 呼び出し待ち状態となる
      Console.WriteLine("{0}で待ってまーす", serverChannel.GetChannelUri());
    //Counterクラス
    public class Counter : MarshalByRefObject {
      private int count = 0;
      public int Count { get {
        return(count++);
    //用意できたら、何かのクリックイベント等で呼び出してみる
      Console.WriteLine("カウント呼び出し {0}",counter.Count);
    

    RemotingConfiguration.RegisterWellKnownServiceType の代わりに
    RemotingServices.Marshal が用いられました。

    登録に用いる引数も、Counterクラス(型) から、Counterクラスのインスタンスに替えます

    B.クライアント側

    //フィールドにインスタンス用の変数を用意(あとで代入される)
    public Counter counter = null;
    //メイン処理
    public void mainProcess(){
      IpcClientChannel clientChannel = new IpcClientChannel();
      ChannelServices.RegisterChannel(clientChannel);
      //処理を差し替え。
      //"ipc://remote/counter" から引っ張ってきた Counterクラスが使えるようになるのは変わ    らない
      var endPointProxy = Activator.GetObject(typeof(Counter), "ipc://remote/counter");
      //変換して使う
      this.counter = (Counter)endPointProxy;
    //Counterクラス (うわべだけ)
    public class Counter : MarshalByRefObject
      public int Count { get { throw null; } }
    //用意できたら、何かのクリックイベント等で呼び出してみる
      Console.WriteLine("カウント呼び出し {0}.", this.counter.Count);
    

    RemotingConfiguration.RegisterWellKnownClientType の代わりに
    Activator.GetObject といったものが出てきました。

    Activator.GetObject() の戻り値は、

    要求した既知のオブジェクトによって提供されたエンドポイントを指すプロキシ

    との事。 (日本語でおk)

    ipc://remote/counter に繋いで引っ張ってきた、Counter型の"なにか"」っていう表現がわかりやすいでしょうか。

    その "なにか" はサーバー側で登録した Counterクラスを実体化させたインスタンスです。

    これをキャストして使う感じになるみたいです。
    前の書き方だと、new Counter() でインスタンス化していましたが、
    今回得るものはクラス(型)ではなく インスタンス自体というのが大きな違いですね

    あと、上記の様に endPointProxy 変数を介さなくても、下記のノリで のっけからキャストするのももちろんアリです

    Counter counter = (Counter)Activator.GetObject(typeof(Counter), "ipc://remote/counter");

    と、まぁこれでプロセス間の通信というか、インスタンス共有ができたことになります
    ( サーバー側のインスタンスをクライアント側が間借りしている、という図式 )

    サーバー側とクライアント側でそれぞれ、counter.Count を呼び出すと
    互いの呼び出した回数が、どちらから呼んでも上乗せされるのが確認できるかと思います。

    インスタンスに限らず、引数も渡せるらしい。書いてみる

    インスタンスが共有できるので、さらに改造して引数を渡せるようにしてみます
    ここでは String をクライアント側がサーバー側に渡せるようにしています
    先ほどと比べると、いささか複雑さを感じるかも (+_+;

    サーバー側

    //フィールドに ShareClass から作ったインスタンスを用意
    public ShareClass shareInstance = new ShareClass();
    //メイン処理
    public void mainProcess()
      IpcServerChannel serverChannel = new IpcServerChannel("hoge_channel");
      ChannelServices.RegisterChannel(serverChannel);
      //インスタンスの共有登録をする前に、
      //前もってインスタンスのフィールドに対して、サーバー側処理のHogeMethodメソッドを登録    しておく。
      shareInstance._fieldMsg += new Counter.CallEventHandler(HogeMethod);
      // shareInstance を「これ使ってね~」と登録。
      // "ipc://hoge_channel/share" で相手方が得られるのは変わらない
      RemotingServices.Marshal(shareInstance, "share", typeof(ShareClass));
    //共有されるインスタンスの実処理
    //最終的に、相手方にこのインスタンスの .Message() を呼んでもらう
    public class ShareClass : MarshalByRefObject {
      private int count = 0;
      // 任意の処理を登録用させるための変数
      public CallEventHandler _fieldMsg;
      // デリゲートを定義して、登録した変数をメソッドとして扱えるようにする
      public delegate void CallEventHandler(string _message);
      // 受け皿に用意したメソッド。ワンクッション置く。
      // _fieldMsg に委譲してある処理(メソッド)に引数を渡して呼ぶ
      public void Message(string _message)
          if (_fieldMsg != null)
              this._fieldMsg(_message);
      public int Count { get { return(count++); }   }
    //共有したインスタンスのMessage()を相手方が呼び出した場合、
    //結果的にサーバー側でこのメソッドに引数が渡され、実行される。
    public void HogeMethod(string _Message)
      Console.WriteLine("相手方から処理が呼び出されました。送られた引数:{0}" , _Message);
    

    クライアント側

    //メイン処理
    public void mainProcess()
      IpcClientChannel icc = new IpcClientChannel();
      ChannelServices.RegisterChannel(icc, false);
      //"ipc://hoge_channel/share" から引っ張ってきた、
      //ShareClassクラスのインスタンスが使えるようになるのは変わらない
      ShareClass shareInstance = (ShareClass)Activator.GetObject(typeof(Counter), "ipc://    hoge_channel/share");
      shareInstance.Message("クライアント側から送るメッセージです。アイアム文字列引数!");
    //ShareClassクラス定義(こちらは中身がなくてOK)
    public class ShareClass : MarshalByRefObject
      public int Count { get { throw null; } }
      public void Message(string sMessage) { }
    

    こんな感じの実装だと、クライアント側でshareInstance.Message()を利用して送ったメッセージを、
    サーバー側の HogeMethod() で受け取れるようになります。

    他参考になったサイト

  • ヘニックスのライブ小屋 - [.NET 2] プロセス通信:サンプル
  • DOBON.NETプログラミング掲示板 - C#→VBへの移植でうまくいきません
  •