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. 臺灣是我的國家