昨天寫了文章《關於Remoting》,感覺有些問題沒有說清楚。後來又看了一些文檔和書,整理了一下,就算是續吧。
其實我發現主要的問題還是集中在用戶端啟用模式。我想再談談用戶端啟用模式和伺服器端啟用模式兩者在代碼實現上的區別。這兩種模式在伺服器監聽程式上的區別不大,前面那篇文章已經說得很清楚了,主要還是用戶端程式。為了讓概念不至於模糊混淆,我下面提到用戶端啟用模式,用Activated;伺服器啟用模式,用WellKnown。
先從VS提供的方法來看:
WellKnown模式:Activator.GetObject()方法。它的傳回值是方法參數裡指定類型的對象執行個體。
ServerRemoteObject.IServerObject serverObj = (ServerRemoteObject.IServerObject)
Activator.GetObject(
typeof(ServerRemoteObject.IServerObject),
"tcp://localhost:8080/ServiceMessage");
Activated模式:
有兩種方法:
1、靜態方法:RemotingConfiguration.RegisterActivatedClientType()。這個方法傳回值為Void,它只是將遠程對象註冊在用戶端而已。具體的執行個體化還需要調用對象類的建構函式。
RemotingConfiguration.RegisterActivatedClientType(
typeof(ServerRemoteObject.ServerObject),
"tcp://localhost:8080/ServiceMessage");
ServerRemoteObject.ServerObject serverObj = new ServerRemoteObject.ServerObject();
2、Activator.CreateInstance()方法。這個方法將建立方法參數指定類型的類對象。它與前面的GetObject()不同的是,它要在用戶端調用建構函式,而GetObject()只是獲得對象,而建立執行個體是在伺服器端完成的。CreateInstance()方法有很多個重載,我著重說一下其中常用的兩個。
1) public static object CreateInstance(Type type, object[] args, object[] activationAttributes);
參數說明:
type:要建立的對象的類型。
args :與要調用建構函式的參數數量、順序和類型匹配的參數數組。如果 args 為空白數組或Null 參考(Visual Basic 中為 Nothing),則調用不帶任何參數的建構函式(預設建構函式)。
activationAttributes :包含一個或多個可以參與啟用的屬性的數組。
這裡的參數args是一個object[]數群組類型。它可以傳遞要建立對象的建構函式中的參數。從這裡其實可以得到一個結論:WellKnown啟用模式所傳遞的遠程對象類,只能使用預設的建構函式;而Activated模式則可以使用者自訂建構函式。
activationAttributes參數在這個方法中通常用來傳遞伺服器的url。
假設我們的遠程對象類ServerObject有個建構函式:
ServerObject(string pName,string pSex,int pAge)
{
name = pName;
sex = pSex;
age = pAge;
}
那麼實現的代碼是:
object[] attrs = {new UrlAttribute("tcp://localhost:8080/ServiceMessage")};
object[] objs = new object[3];
objs[0] = "wayfarer";
objs[1] = "male";
objs[2] = 28;
ServerRemoteObject.ServerObject = Activator.CreateInstance(
typeof(ServerRemoteObject.ServerObject),
objs,attrs);
可以看到,objs[]數組傳遞的就是建構函式的參數。
2、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);
參數說明:
-
assemblyName
-
將在其中尋找名為
typeName 的類型的程式集的名稱。如果
assemblyName 為空白引用(Visual Basic 中為
Nothing),則搜尋正在執行的程式集。
-
typeName
-
首選類型的名稱。
-
activationAttributes
-
包含一個或多個可以參與啟用的屬性的數組。
參數說明一目瞭然。注意這個方法傳回值為ObjectHandle類型,因此代碼與前不同::
object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};
ObjectHandle handle = Activator.CreateInstance("ServerRemoteObject","ServerRemoteObject.ServerObject",attrs);
ServerRemoteObject.ServerObject obj = (ServerRemoteObject.ServerObject)handle.Unwrap();
那麼,這個方法實際上是調用的預設建構函式。ObjectHandle.Unwrap()方法是返回被封裝的對象。
說明:要使用UrlAttribute,還需要在命名空間中添加:using System.Runtime.Remoting.Activation;
通過這些代碼的比較,我們還可以得到一個不幸的結論:
對於Activated啟用模式,不管是使用靜態方法,還是使用CreateInstance()方法,都必須在用戶端調用建構函式執行個體化對象。這樣一來,在用戶端我們提供的遠程對象,就不可能只提供介面,而沒有類的實現。而在WellKnown模式,因為在用戶端只是用GetObject()獲得對象,執行個體化是在伺服器端完成的。所以用戶端我們只提供介面就夠了。
所以對於Activated模式,我們必須在伺服器和用戶端提供兩份完全相同的遠程對象Dll,這個結果確實讓人很沮喪。有沒有其他方法實現呢?鑒於它的實現原理,答案顯然是否定的。我看了MSDN上的文章,唯一的可行方案就是利用前文提到的用工廠的方法。要注意的是:這種方法是一種利用WellKnown模式來類比Activated模式,是一種方法的折中。
前文說過,伺服器端遠程對象,提供兩個介面。一個介面是具體要傳遞的遠程對象的介面,一個就是工廠介面。還必須有個工廠類實現工廠介面,提供建立遠程對象執行個體的方法。
public interface IServerObject
{
Person GetPersonInfo(string name,string sex,int age);
}
public interface IServerObjFactory
{
IServerObject CreateInstance();
}
public class ServerObject:MarshalByRefObject,IServerObject
{
public Person GetPersonInfo(string name,string sex,int age)
{
Person person = new Person();
person.Name = name;
person.Sex = sex;
person.Age = age;
return person;
}
}
public class ServerObjFactory:MarshalByRefObject,IServerObjFactory
{
public IServerObject CreateInstance()
{
return new ServerObject();
}
}
而用戶端呢,只提供介面就可以了。
public interface IServerObject
{
Person GetPersonInfo(string name,string sex,int age);
}
public interface IServerObjFactory
{
IServerObject CreateInstance();
}
然後我們用WellKnown啟用模式,在伺服器端:
//傳遞對象;
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ServerRemoteObject.ServerObjFactory),
"ServiceMessage",WellKnownObjectMode.SingleCall);
注意這裡註冊的不是ServerObject類對象,而是ServerObjFactory類對象。
用戶端:
ServerRemoteObject.IServerObjFactory serverFactory = (ServerRemoteObject.IServerObjFactory)
Activator.GetObject(
typeof(ServerRemoteObject.IServerObjFactory),
"tcp://localhost:8080/ServiceMessage");
ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();
首先用GetObject()返回工廠介面,再利用該介面對象調用工廠的方法CreateInstance()來建立IServerObject對象。
也許有人會納悶了,這裡使用的是WellKnown模式啊,為什麼說是Activated模式呢?是這樣的,本質來說,它是WellKnown模式。但由於我們利用了工廠來建立執行個體,因此感覺上是在用戶端來建立具體的遠程對象執行個體。因此,我說這是一種方法的折中。
好了,寫到這裡,自認為交待清楚了。但Remoting的實現遠不止於此,還有很多進階複雜的東西我們還沒有用到。例如自訂通道,自訂MarshalByReferenceObject衍生類別,生命週期管理,自訂代理。這些東西我也弄不清楚。如果以後弄明白了,希望能寫點東西。