[.Net] 使用remoting(七)由Server觸發Client事件

Taiwan is an independent country.

一直想簡化它的架構,但還是簡化不了~該有的東西還是要有...
由於要由Server來觸發所有Client事件,所以Client必須註冊事件(這是一定要的啦~),而且也必須要讓Server知道client有註冊事件,所以呀...唉~轉個彎才能作到~但一開始的作法和第四篇有點像...

1. 建立4個專案分別為

 client(本範例out type選Console Application), 
 proxy(out type:Class Library), 
   server(out type:Class Library)
   serverUI(本範例out type選Console Application), 
proxy用來對應client和server端,所以2端都要放置; 程式運作是Interface被server端的Class實作,client藉由通道抓取server端實作出的Class建立Interface實體
而serverUI只是為了架設並啟動server用而已
 
2. 幫專案加參考:client, serverUI加入參考System.Runtime.Remoting
  client參考proxy; server參考proxy; serverUI參考server,proxy

3. 在proxy專案加入一個Interface:

    public delegate void SEventHandler(String say);
    public interface IPxy
    { event SEventHandler AftSay;    }

4. 在server專案加入一個給值用的class:

    public class SrvSay : MarshalByRefObject, IPxy
    {
        public event SEventHandler AftSay;

        public void Say(String say)
        {
            if (AftSay != null)
            {
                foreach (SEventHandler e in AftSay.GetInvocationList())
                {
                    try//一一傳至client
                    { e(say); }
                    catch
                    {
                        Console.WriteLine("無法與對方連線");
                        AftSay -= e;//移除不能用的
                    }
                }
            }
        }

        public override object InitializeLifetimeService()
        { return null; }//生命週期改為無限大
    }

5. 在serverUI專案修改Program.cs內的Main方法,以便啟動server:

 
       static void Main()
        {
            try
            {
                //程式設定,也可依第二篇用Config設定, 其中的<server>這段要去掉,且
                //<channel port="7778" ref="http server">要改為<channel port="7778" ref="http">
                IDictionary p = new Hashtable() { };
                p["port"] = 7778;
                ChannelServices.RegisterChannel(new HttpChannel(p
                    , new BinaryClientFormatterSinkProvider()
                    , new BinaryServerFormatterSinkProvider() { TypeFilterLevel = TypeFilterLevel.Full }), false);
                SrvSay sp = new SrvSay();
                RemotingServices.Marshal(sp, "SrvName");
                string s;
                while ((s = Console.ReadLine()) != "")
                { sp.Say(s); }
            }
            catch (Exception e)
            { Console.WriteLine(e.Message); }
            Console.ReadLine();
        }
 
 
6. 在proxy專案加入一個Class:註冊Server的事件,再觸發自己的事件
    public class SrvPxy : MarshalByRefObject, IPxy
    {
        public SrvPxy()//下句為產生實例與註冊元件,可以只寫在Client端
        { ((IPxy)Activator.GetObject(typeof(IPxy), "http://localhost:7778/SrvName")).AftSay += Say; }
         //要注意的是註冊的method(本範例是SrvPxy的Say)必須client與server皆有, 且有繼承MarshalByRefObject
        public event SEventHandler AftSay;

        public void Say(String say)
        {
            if (AftSay != null)
            { AftSay(say); }
        }

        public override object InitializeLifetimeService()
        { return null; }//生命週期改為無限大
    }

7. 在client專案修改Program.cs內容,以便呼叫server:

        static void Main()
        {
            IPxy tt = null;
            try
            {
                //1. 程式設定,也可依第二篇用Config設定, 其中的<client>這段要去掉,且
                //<channel ref="http client">要改為<channel port="10" ref="http">
                IDictionary p = new Hashtable() { };
                p["port"] = 10;//port是給事件用的,接收server資料,只要不跟server開的port相同即可
                ChannelServices.RegisterChannel(new HttpChannel(p
                    , new BinaryClientFormatterSinkProvider()
                    , new BinaryServerFormatterSinkProvider() { TypeFilterLevel = TypeFilterLevel.Full }), false);
                //SrvName和port要與server相同哦~localhost也請改成server的ip
                tt = new SrvPxy();
                tt.AftSay += tt_AftSay;
            }
            catch (Exception e)
            { Console.WriteLine(e.Message); }
            Console.ReadLine();
            if (tt != null) tt.AftSay -= tt_AftSay;//離開請主動移除
        }

        static void tt_AftSay(String say)
        { Console.WriteLine(say); }

7. 整個方案編譯後, 先執行serverUI.exe(位置在serverUI專案目錄的bin/debug下),將server啟動;再執行client.exe(位置在client專案目錄的bin/debug下),若有成功呼叫到server,會每次執行都遞增

 
本範列另外delegate一個SEventHandler來使用,其實想直接用EventHandler也是可以的~
 

 

Taiwan is a country. 臺灣是我的國家