Remoting基礎
基本原理
當用戶端創建遠端remotableclass的一個實例,.net框架在用戶端應用程式域中產生一個代理。該代理看起來就像實際物件。代理收到調用後,通過通道連接到遠端的物件。
一、編寫步驟
第一步
編寫一個dll,其中包含所要remottable的類
public class remotableclass:marshalbyrefobject
{
….
}
第二步 {
….
}
伺服器進程註冊該remotable 類以便其他應用程式可以啟動。根據該物件是如何啟動,伺服器通過兩種靜態方法來註冊:registeractivatedservicetype或者 registerwellknownservicetype。下面的語句使用registerwellknownservicetype來註冊 remotableclass,以便遠端啟動。
remotingconfiguration.registerwellknownservicetype(
typeof(remotableclass), //remotable類
“remoteobject”, // remotable類的uri
wellknownobjectmode.singlecall); //啟動模式
第一個參數是指能遠端化的類。 typeof(remotableclass), //remotable類
“remoteobject”, // remotable類的uri
wellknownobjectmode.singlecall); //啟動模式
第二個是指用戶端使用來啟動物件的uri----也就是用戶端告訴伺服器來啟動
remotableclass實例的uri。
第三個參數指定啟動模式。有兩種選擇。wellknownobjectmode.singlecall是指為用戶端的每一次調用創建一個新的實 例。wellknownobjectmode.singleton是指創建一個remotableclass實例來處理所有用戶端的調用。
第三步
為了使用戶端可以使用remotableclass,伺服器進程必須創建,註冊一個通道。該通道提供物件和遠端用戶端交流的一個管道。在伺服器端,.net框架提供了兩種通道:
system.runtime.remoting.channels.tcp.tcpserverchannel:可以接受遠端用戶端的tcp連接。
system.runtime.remoting.channels.http.httpserverchannel:接受http連接。
下面的語句創建一個在1234埠監聽的tcpserverchannel通道,並用.net框架註冊:
tcpserverchannel channel = new tcpserverchannel(1234);
channelservices.registerchannel(channel);
下面的語句註冊了一個在1234埠監聽的http通道:
channelservices.registerchannel(channel);
httpservicechannel channel = new httpserverchannel(1234);
channelservices.registerchannel(channel);
tcpserverchannel更有效率一點。httpserverchannel是使用iis作為遠端啟動代理時使用的選擇。 channelservices.registerchannel(channel);
第四步
在用戶端要想創建遠端類的一個實例,也必須做一些註冊。
第一必須註冊一個用戶端通道。.net框架提供了兩種類型的用戶端通道:tcpclientchannel和httpclientchannel,分別和伺服器端通道相對應。
第二,如果用戶端想使用new操作符來生產遠端物件,必須將遠端物件註冊到本地應用程式域。
remotingconfiguration.registerwellknownclienttype是在用戶端註冊一個類。
remotingconfiguration.registerwellknownservicetype是在伺服器上註冊一個類。
下面的代碼在用戶端註冊了一個tcp通道,而且也將remotableclass註冊到本地應用程式域中:
tcpclientchannel channel = new tcpclientchannel();
channelservices.registerchannel(channel);
remotingconfiguration.registerwellknownclienttype(
typeof(remotableclass),
“tcp://localhost:1234/remoteobject”);
第二個參數是指遠端對象的url。 channelservices.registerchannel(channel);
remotingconfiguration.registerwellknownclienttype(
typeof(remotableclass),
“tcp://localhost:1234/remoteobject”);
協定必須匹配應用程式註冊的通道協定。
可以使用機器名或者ip位址來替換localhost。
埠數必須好伺服器端監聽的埠數一樣。
物件uri,必須和伺服器用registerwellknownservicetype註冊的匹配。
第五步
在用戶端使用new來產生代理:
remotableclass rc = new remotableclass();
這個操作在用戶端應用程式域中產生一個代理,返回remotableclass的一個引用。 二、實際範例
clockserver.cs
using system;
public class clock : marshalbyrefobject
{
public string getcurrenttime ()
{
return datetime.now.tolongtimestring ();
}
}
public class clock : marshalbyrefobject
{
public string getcurrenttime ()
{
return datetime.now.tolongtimestring ();
}
}
timeserver.cs
using system;
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpserverchannel channel = new tcpserverchannel (1234);
channelservices.registerchannel (channel);
remotingconfiguration.registerwellknownservicetype
(typeof (clock), "clock", wellknownobjectmode.singlecall);
console.writeline ("press enter to terminate");
console.readline ();
}
}
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpserverchannel channel = new tcpserverchannel (1234);
channelservices.registerchannel (channel);
remotingconfiguration.registerwellknownservicetype
(typeof (clock), "clock", wellknownobjectmode.singlecall);
console.writeline ("press enter to terminate");
console.readline ();
}
}
timeclient.cs
using system;
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpclientchannel channel = new tcpclientchannel ();
channelservices.registerchannel (channel);
remotingconfiguration.registerwellknownclienttype
(typeof (clock), "tcp://localhost:1234/clock");
clock clock = new clock ();
console.writeline (clock.getcurrenttime ());
}
}
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpclientchannel channel = new tcpclientchannel ();
channelservices.registerchannel (channel);
remotingconfiguration.registerwellknownclienttype
(typeof (clock), "tcp://localhost:1234/clock");
clock clock = new clock ();
console.writeline (clock.getcurrenttime ());
}
}
編譯:
1. csc /t:library clockserver.cs
2. csc /r:clockserver.dll timeserver.cs
3. csc /r:clockserver.dll timeclient.cs
要將clockserver.dll拷貝到用戶端。因為創建遠端物件的代理時,.net框架需要描述clock類的原資料。它可以從dll中得到原資料。 2. csc /r:clockserver.dll timeserver.cs
3. csc /r:clockserver.dll timeclient.cs
三、配置方式
timeserver和timeclient在其源代碼內部註冊通道和遠端化的類。這樣有個缺點,一旦任何一個註冊資料改變,你必須要修改源代碼,並重新編譯。 這就是為什麼.net框架支持另一種形式的註冊。聲明註冊是通過調用靜態
remotingconfiguration.configure方法來從config檔中得到資訊。
範例如下:
clockserver.cs
using system;
public class clock : marshalbyrefobject
{
public string getcurrenttime ()
{
return datetime.now.tolongtimestring ();
}
}
public class clock : marshalbyrefobject
{
public string getcurrenttime ()
{
return datetime.now.tolongtimestring ();
}
}
timeserver.cs
using system;
using system.runtime.remoting;
class myapp
{
static void main ()
{
remotingconfiguration.configure ("timeserver.exe.config");
console.writeline ("press enter to terminate");
console.readline ();
}
}
using system.runtime.remoting;
class myapp
{
static void main ()
{
remotingconfiguration.configure ("timeserver.exe.config");
console.writeline ("press enter to terminate");
console.readline ();
}
}
timeserver.exe.config
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown mode="singlecall" type="clock, clockserver"
objecturi="clock" />
</service>
<channels>
<channel ref="tcp server" port="1234" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown mode="singlecall" type="clock, clockserver"
objecturi="clock" />
</service>
<channels>
<channel ref="tcp server" port="1234" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
timeclient.cs
using system;
using system.runtime.remoting;
class myapp
{
static void main ()
{
remotingconfiguration.configure ("timeclient.exe.config");
clock clock = new clock ();
console.writeline (clock.getcurrenttime ());
}
}
using system.runtime.remoting;
class myapp
{
static void main ()
{
remotingconfiguration.configure ("timeclient.exe.config");
clock clock = new clock ();
console.writeline (clock.getcurrenttime ());
}
}
timeclient.exe.config
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown type="clock, clockserver"
url="tcp://localhost:1234/clock" />
</client>
<channels>
<channel ref="tcp client" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
該方式的缺點是配置檔可以被修改和刪除。 <system.runtime.remoting>
<application>
<client>
<wellknown type="clock, clockserver"
url="tcp://localhost:1234/clock" />
</client>
<channels>
<channel ref="tcp client" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
四、啟動方式
.net框架將可遠端化物件分為兩種:伺服器啟動物件和用戶端啟動物件。
伺服器端啟動物件是通過remotingconfiguration’sregisterwellknownservicetype和 registerwellknownclienttype方法註冊的。上面的範例都是伺服器端啟動物件。用戶端啟動對象是通過registeractivateservicetype和registeractivatedclienttype註冊的。伺服器端啟動物件被稱為伺服器啟動的,因為當用戶端使用new,只有一個代理被創建。實際物件知道通過代理來調用一個方法時才被創建(啟動)。換句話說, 不是用戶端決定什麼時候去創建物理上的真正物件。用戶端啟動物件在用戶端使用new時就在伺服器上創建。這個是第一個差別。第二個差別是用戶端啟動物件可以使用非缺省構造函數(帶參數的構造函數)啟動。伺服器端機會物件不支援非缺省構造函數,因為使用new只是創建一個代理,並沒有創建對應的實際物件。用戶端啟動物件可以通過new同時創建代理和物件。 第三個差別是用戶端和物件是如何聯繫在一起的。當註冊伺服器啟動物件時,你可以指定啟動模式來決定為每一個請求創建一個物件實例還是創建一個物件實例來服務所有的請求。這兩中啟動模式是:
wellknownobjectmode.singlecall:為每個請求創建一個唯一的物件實例。
wellkonwnobjectmode.singleton:創建一個物件實例來服務所有的請求
通常根據環境來選擇合適的啟動模式。舉例來說,如果一個遠端化物件提供了一個”one-shot”服務,不需要在多次調用間保持狀態或者不需要在所有客戶 端同享狀態,那麼singlecall是個正確的選擇。因為每一次的請求產生的是一個新的物件實例。如果想在用戶端之間傳遞資料,則要使用 singleton。 singleton物件一個值得注意的地方是線程的同步問題。當兩個用戶端同時調用該物件的方法時,可能會出現錯誤,這時要使用.net框架提供的同步機制。
用戶端啟動物件提供第三種選擇。當使用用戶端啟動物件時,該物件僅為此用戶端服務,可以在多次調用間保持狀態。
single-call伺服器啟動物件,singleton伺服器啟動物件和用戶端啟動物件的提供了三種不同的啟動模式。當不需要在所有用戶端共用狀態 時,則使用single-call。當要在所有用戶端共用狀態時則使用singleton。當不需要所有的用戶端連接到同一個物件,只要保持該用戶端自己 的狀態時,則使用用戶端啟動物件。
程式範例:
stopwatch.cs
using system;
public class stopwatch : marshalbyrefobject
{
datetime mark = datetime.now;
public void start ()
{
mark = datetime.now;
}
public int stop ()
{
return (int) ((datetime.now - mark).totalmilliseconds);
}
}
public class stopwatch : marshalbyrefobject
{
datetime mark = datetime.now;
public void start ()
{
mark = datetime.now;
}
public int stop ()
{
return (int) ((datetime.now - mark).totalmilliseconds);
}
}
stopwatchserver.cs
using system;
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpserverchannel channel = new tcpserverchannel (1234);
channelservices.registerchannel (channel);
remotingconfiguration.registeractivatedservicetype
(typeof (stopwatch));
console.writeline ("press enter to terminate");
console.readline ();
}
}
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpserverchannel channel = new tcpserverchannel (1234);
channelservices.registerchannel (channel);
remotingconfiguration.registeractivatedservicetype
(typeof (stopwatch));
console.writeline ("press enter to terminate");
console.readline ();
}
}
stopwatchclient.cs
using system;
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpclientchannel channel = new tcpclientchannel ();
channelservices.registerchannel (channel);
remotingconfiguration.registeractivatedclienttype
(typeof (stopwatch), "tcp://localhost:1234");
stopwatch sw = new stopwatch ();
sw.start ();
console.writeline ("press enter to show elapsed time");
console.readline ();
console.writeline (sw.stop () + " millseconds");
}
}
using system.runtime.remoting;
using system.runtime.remoting.channels;
using system.runtime.remoting.channels.tcp;
class myapp
{
static void main ()
{
tcpclientchannel channel = new tcpclientchannel ();
channelservices.registerchannel (channel);
remotingconfiguration.registeractivatedclienttype
(typeof (stopwatch), "tcp://localhost:1234");
stopwatch sw = new stopwatch ();
sw.start ();
console.writeline ("press enter to show elapsed time");
console.readline ();
console.writeline (sw.stop () + " millseconds");
}
}
五、activator.getobject和activator.createinstance方法
new操作符並不是啟動遠端物件的唯一方法。.net框架提供了其他的啟動方法:getobject和createinstance。它們都是 system.activator類的成員。getobject被用來啟動在伺服器端啟動的物件,而createinstance被用來啟動在用戶端啟動 的物件。
當使用getobject或者createinstance來啟動遠端物件時,不再需要調用registeractivatedclienttype或者 registerwellknownclienttype來註冊伺服器上可遠端化的類。例如:啟動在伺服器端啟動的物件時:
remotingconfiguration.registerwellknownclienttype(typeof(clock),”tcp://localhost:1234/clock”);
clock clock = new clock();
clock clock = new clock();
可以使用下面的方法代
clock clock =(clock) activator.getobject(typeof(clock,”tcp://localhost:1234/clock”);
啟動用戶端物件時:
remotingconfiguration.registeractivatedclienttype(typeof(stopwatch),”tcp://localhost:1234”);
stopwatch sw = new stopwatch();
stopwatch sw = new stopwatch();
可以這樣的方式:
object[] url ={new urlattribute(“tcp://localhost:1234”)};
stopwatch sw =(stopwatch) activator.createinstance(typeof(stopwatch),null,url);
stopwatch sw =(stopwatch) activator.createinstance(typeof(stopwatch),null,url);
為什麼要使用它們來代替new呢?因為在你僅知道url和介面時,getobject和createinstance可以仍使用。假設改變clock類,它實現一個iclock介面。
使用getobject時:
iclock ic = (iclock)activator.getobject(typeof(iclock),”tcp://localhost:1234/clock”);
如果使用new,則會出現編譯錯誤,因為new不能接受一個介面名稱:
remotingconfiguration.registerwellknownclienttype
(typeof (iclock), "tcp://localhost:1234/clock");
iclock ic = new iclock ();
(typeof (iclock), "tcp://localhost:1234/clock");
iclock ic = new iclock ();
六、物件生存期和租用期
一個single-call伺服器端啟動物件只在方法調用期間生存。之後,被垃圾回收器標記為刪除。singleton 伺服器啟動物件和用戶端啟動物件不一樣,他們的生存期被租用控制。租用是一個物件,它實現了定義在 system.runtime.remoting.lifetime名稱空間的ilease介面。
singleton 伺服器端啟動物件和用戶端啟動物件缺省的租用物件有一個5分鐘的initialleasetime,2分鐘的renewoncalltime,5分鐘的 currentleasetime。如果物件沒有方法被調用,當currentleasetime為0時它被清除,也就是5分鐘後被清除。
源文地址﹕
http://www.z6688.com/info/35447-1.htm