830 likes | 1.03k Views
Remoting. 杨圣洪 hbxxysh2@sohu.com. 为什么要学习 Remoting. 想 在 WEB 服务中直接调用 EXE 组件程序,如Word与Excel , csc test.cs 。均无法实现,所以试着: 1) 把 exe 放到 webService 目录下,允许写权限。 在文件夹上添加了 aspnet 所有权限。 2)IIS 使用 windows 身份验证 , 客户端加 NetworkCredential, 使用 administrator 账户 . 3) 在服务中,将“ ASP.NET 状态服务”登录设置“允许与桌面交互”。
E N D
Remoting 杨圣洪 hbxxysh2@sohu.com
为什么要学习Remoting 想在WEB服务中直接调用EXE组件程序,如Word与Excel, csc test.cs。均无法实现,所以试着: 1)把exe放到webService目录下,允许写权限。 在文件夹上添加了aspnet所有权限。 2)IIS使用windows身份验证, 客户端加NetworkCredential,使用administrator账户. 3)在服务中,将“ASP.NET 状态服务”登录设置“允许与桌面交互”。 4) 设置“服务”中“IIS Admin”之“登录”,勾选“服务与桌面交互”,重启IIS 5)在web.config里面模拟里管理员帐户。 <identity impersonate="true" userName="xx\Administrator" password="***" />
为什么要学习Remoting 1)建虚拟目录wsRemote1 2)建Web服务http://localhost/wsRemote1 3)添加如下WebMethod: [WebMethod] public void RemotCompile(string srcFileName){ System.Diagnostics.ProcessStartInfo info=new ProcessStartInfo(); info.FileName = "cmd.exe"; info.UseShellExecute =false; info.RedirectStandardInput =true; info.RedirectStandardOutput =true; info.RedirectStandardError = true; info.CreateNoWindow = true; string cmdStr = "regedit"; //"csc "+srcFileName; System.Diagnostics.Process proc=System.Diagnostics.Process.Start(info); proc.StandardInput.WriteLine(cmdStr); proc.StandardInput.WriteLine("exit"); }
为什么要学习Remoting 键入http://localhost/wsremote1/service1.asmx,看不到
为什么要学习Remoting 键入http://localhost/wsremote1/service1.asmx,看不到 在“服务”中将将“ASP.NET 状态服务”登录设置“允许与桌面交互”,不起作用! 在“服务”中将将“IIS Admin”登录设置“允许与桌面交互”,不起作用! 所以只好寻求新方法,想到曾成功配置过DCOM,故选用新版DCOM,即.NET平台中的Remoting。 WEB服务是互联网上调用服务的技术,利用.NET Remoting技术,可调用服务器中的方法。
为什么要学习Remoting Remoting技术使用任何类型应用程序定义域中的服务,包括了主控台应用程序、Windows Form、Internet Information Services (IIS)、XML Web Service 或 Windows 服务。 采用二进制格式通讯。 以传址方式(By Reference)传递对象,并传回特定应用程序定义域中的特定对象。 必须直接控制启动过程、对象的生命期。 可定制信道或通讯协议,满足特定需求。 直接参与通讯处理,以建立所需的功能。 建立更复杂的应用程序类型
Remoting基本概念 一、对象类型 1、单次调用对象(Single Call object) 调用一次后被释放,无状态。 2、单一对象(singleton object)单个实例 多客户端共1个服务端,有状态,COM组件类似 可节省服务器的内存资源等,提高响应速度 3、客户端激活(client-activated object) 客户端调用方法时才激活,这与COM的激活方式一样,随着激活客户端的死亡而消失,如果激活者一直在线又不使用服务端对象,这时造成资源浪费,因此有各种生存期控制方式。 COM组件是综合了(2)(3)二种激活方式。
Remoting基本概念 二、托管程序Managed 用C++编写了.net的执行环境CLR,即C:\WINDOWS\Microsoft.NET\Framework中的各种程序, 用.net编写的程序都在该环境中运行, 该环境提供了托管堆、垃圾回收、JIT编译等服务。 用非.net编写的程序称为非托管程序,它与托管程序之间可以互相调用,但要进行相应的设置。 三、应用程序域AppDomain 不同的应用程序运行在不同程序域中。 但不同应用程序可以共同一个程序域中。 每个正在执行的程序默认有一个程序域。
Remoting基本概念 三、应用程序域AppDomain 1)获取当前程序所属域 AppDomain currentDomain = AppDomain.CurrentDomain; AppDomain currentDomain = Thread.GetDomain(); 2)获取当前程序所属域的名称 string name = AppDomain.CurrentDomain.FriendlyName; 3)创建新域 AppDomain newDomain = AppDomain.CreateDomain("NewD"); 4)在应用程序域中创建对象 DemoClass obj = (DemoClass)AppDomain.CurrentDomain.CreateInstanceAndUnWrap("ClassLib", "ClassLib.DemoClass");//dll文件名 名字空间.类名 ObjectHandle obj2= AppDomain.CurrentDomain.CreateInstance("ClassLib", "ClassLib.DemoClass");DemoClass obj = (DemoClass)obj2.UnWrap();
应用程序域实例 1、建立类库项目ClassLib2 using System; namespace ClassLib2{ public class DemoClass { private int count=0; public DemoClass(){ Console.WriteLine("DemoClass的对象已创建");} public void ShowCount() { count++; Console.WriteLine("count={0},所在域为={1}",this.count, AppDomain.CurrentDomain.FriendlyName);} public void ShowAppDomain() { AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("服务端所在程序域={0}",cd.FriendlyName); } }}
应用程序域实例 2、建立控制台ClassLib2ClientA并引用ClassLib2 using System; //添加引用 using ClassLib2; namespace ClassLib2ClientA{ class Class1 { [STAThread] static void Main(string[] args) { AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("客户端程序域={0}",cd.FriendlyName); ClassLib2.DemoClass obj=(DemoClass)cd.CreateInstanceAndUnwrap( "ClassLib2","ClassLib2.DemoClass"); Console.WriteLine("构造函数被执行吗?"); obj.ShowAppDomain();//该方法是在ClassLib2的域中 obj.ShowCount(); //还是在ClassLib2ClientA的域中执行 Console.ReadLine();}}}
应用程序域实例 3、执行ClassLib2ClientA,可看到效果为: ClassLib2.dll它所在程序域,与ClassLib2ClientA.exe所在一样,说明DLL与调用方在同一个进程中。 如果在ClassLib2ClientA.exe中新建程序域,再调用ClassLib2.dll中的方法,会出现“ClassLib2.DemoClass 未标记为可序列化”错!为了试验该错误,而新建控制台ClassLib2ClientB.exe
应用程序域实例 4、建立控制台ClassLib2ClientB并引用ClassLib2 using System; using ClassLib2; namespace ClassLib2ClientB{ class Class1 { [STAThread] static void Main(string[] args) { AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("客户端程序域={0}",cd.FriendlyName); AppDomain nd=AppDomain.CreateDomain("新域"); DemoClass obj=nd.CreateInstanceAndUnwrap("ClassLib2", "ClassLib2.DemoClass") as DemoClass; obj.ShowAppDomain(); obj.ShowCount(); Console.ReadLine();} }} 运行时肯定出错,为此新建ClassLib3.dll实现Serializable
应用程序域实例 5、建立类库项目ClassLib3 using System; namespace ClassLib3{ [Serializable] public class DemoClass { private int count=0; public DemoClass(){ Console.WriteLine("类DemoClass的构造函数被执行"); AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("构造函数中的服务端所在程序域={0}",cd.FriendlyName); } public void ShowCount(){ count++; Console.WriteLine("count={0}",this.count);} public void ShowAppDomain() { AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("执行服务端函数时所在程序域={0}",cd.FriendlyName); }}}
应用程序域实例 6、建立控制台ClassLib3ClientA并引用ClassLib3 using System; using ClassLib3; namespace ClassLib3ClientA{ class Class1 { [STAThread] static void Main(string[] args){ AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("客户端程序域={0}",cd.FriendlyName); AppDomain nd=AppDomain.CreateDomain("新域"); DemoClass obj=nd.CreateInstanceAndUnwrap("ClassLib3", "ClassLib3.DemoClass") as DemoClass; obj.ShowAppDomain(); obj.ShowCount(); Console.ReadLine();} }} //这时能在“新域”中,正常调用ClassLib3.dll。
应用程序域实例 7、执行ClassLib3ClientA,可看到效果为: ClassLib3.dll之类DemoClass的实例在新域被构造。 其方法却运行于客户端ClassLib3ClientA.exe所在域中 说明 “序列化”起作用了, 已将DemoClass的实例,整体传送到客户方域中。 ClassLib2ClientA.exe与ClassLib2.dll同域 ClassLib2ClientB.exe与ClassLib2.dll不同域 ClassLib3ClientA.exe与ClassLib3.dll不同域,但已序列化
代理proxy与封送Marshalling 8、代码中 方式一DemoClass obj=new DemoClass(); 方式二DemoClass obj= nd.CreateInstanceAndUnwrap("ClassLib3", "ClassLib3.DemoClass") as DemoClass; 方式一是在CLR即执行环境的“托管堆”中创建对象,犹如在本地创建对象一样。 方式二创建2个对象,一个在"新域"中,一个在客户方所在程序域ClassLib3ClientA.exe中。 在“新域”中创建对象后,对该对象进行序列化、封送到ClassLib3ClientA.exe域中,再解封并反序列化, 在客户方重新构建该对象,相当于进程迁移
代理proxy与封送Marshalling 8、 在“新域”中创建对象后,对该对象进行序列化、并且封送,送到ClassLib3ClientA.exe域中,再解封并反序列化,在客户方重新构建该对象 。付出代价 以上那种在本地重构远程对象的方式,称为传值封送(Marshal by Value),采用方法CreateInstanceAndUnwrap重构远程对象的方法很少使用,传值封送仅用来传递数值等标量数据对象,代价很高! 一般在客户方只创建远程对象的本地代理,这时需要远程对象的元数据metadata即接口信息,不需要远程对象的实现代码,这与COM组件是一样的。 对于客户方而言,本地代理就是远程对象。 好比长沙海尔服务部,就代表海尔公司。
代理proxy与封送Marshalling 一般在客户方只创建远程对象的本地代理,这时需要远程对象的元数据metadata即接口信息,不需要远程对象的实现代码,这与COM组件是一样的。 这时需要对远程对象类的定义进行修改 去掉ClassLib3.dll中的[Serializable], 修改其基类为MarshalByRefObject 如果有[Serializable]则在客户程序域中,重建被调用的对象! 为此新建类库文件ClassLib4,其代码如下:
代理proxy与封送Marshalling 9、新建类库文件ClassLib4.dll,其代码如下: using System; namespace ClassLib4{ public class DemoClass:System.MarshalByRefObject { private int count=0; public DemoClass() { AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("类DemoClass的对象构造之域:"+ cd.FriendlyName);} public void ShowCount() { count++; Console.WriteLine("count={0}",this.count);} public void ShowAppDomain(){ AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("调用服务端程序域={0}",cd.FriendlyName); } }}
代理proxy与封送Marshalling 10、新建ClassLib4.dll的客户端ClassLib4ClientA,: using System; using ClassLib4;//添加引用ClassLib4 namespace ClassLib4ClientA{ class Class1 { [STAThread] static void Main(string[] args){ AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine("客户端程序域={0}",cd.FriendlyName); AppDomain nd=AppDomain.CreateDomain("新域"); DemoClass obj=nd.CreateInstanceAndUnwrap("ClassLib4", "ClassLib4.DemoClass") as DemoClass;//新域创建对象 obj.ShowAppDomain();//调用远程对象的方法 obj.ShowCount(); Console.ReadLine();} }}
应用程序域实例 11、执行ClassLib4ClientA,可看到效果为: 构造远程对象,调用远程对象的方法均在“新域”, 与ClassLib3ClientA.exe不一样, ClassLib3.dll中 “序列化”,整体传送到客户方域中MBV ClassLib4.dll中未序列化,仅在客户方建立远程对象的代理Marshal By Reference,对象仍在远程域中执行。
Remoting的体系结构 12、前面学习远程对象的类型、程序域的概念、跨程序域就是远程调用。方法有: 传送有传值封送MBV(远程对象要Serializable)、 传引用封送MBR(远程对象派生于MarshalByRefObject) Remoting技术是一种跨域调用技术,它采用MBR调用远程对象,本地创建远程对象代理,方法执行仍在远程域中,其基本组成为: (1)派生于MarshalByRefObject远程对象,保存在类库文件中。 (2)远程对象的执行环境或宿主程序,可为多种形式。 需要用到Channel、Formatter、StackBuilder. (3)客户方可为多种形式。 需要用到proxy、 Channel 、Formatter。
远程服务组件(程序集) 12、 建立类库文件ClassLib5 using System; namespace ClassLib5{ public class DemoClass:MarshalByRefObject { //MBR方式发送引用到客户端 private int count = 0; public DemoClass() { AppDomain cd = AppDomain.CurrentDomain; Console.WriteLine(" 远程对象构造时:"+cd.FriendlyName);} public void ShowCount(){ count++;Console.WriteLine("类中count={0}.", count);} public void ShowAppDomain(){ AppDomain cd = AppDomain.CurrentDomain; Console.WriteLine("执行远程对象的域="+cd.FriendlyName);} public int GetCount() { return count;}}}
远程服务程序集的执行环境 13、ClassLib5.dll执行环境ClassLib5Srv.exe(dosapp) 执行“添加引用”选择ClassLib5.dll,还选择如下dll(版本) C:WINDOWS\Microsoft.NET\Frameworkv\1.4322\System.Runtime.Remoting.dll using System;//只有此句是向导生成 using ClassLib5; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Http; namespace ClassLib5Srv{ class Class1{ [STAThread] static void Main(string[] args) {….} }}
远程服务程序集的执行环境 13、ClassLib5.dll执行环境ClassLib5Srv.exe(dosapp) namespace ClassLib5Srv{ class Class1{ [STAThread] static void Main(string[] args) { IChannelReceiver tcpChn1=new TcpChannel(8601);//tcp ChannelServices.RegisterChannel(tcpChn1); //注册通道 IChannel httpChn1=new HttpChannel(8602);//http ChannelServices.RegisterChannel(httpChn1); RemotingConfiguration.ApplicationName="Remote1"; Type t=typeof(ClassLib5.DemoClass); //注册服务类,客户方激活方式,可自定义构造函数,有状态 RemotingConfiguration.RegisterActivatedServiceType(t); Console.WriteLine("远程对象就绪");Console.Read(); }}} //客户方呼叫指定服务器的指定通道
远程服务程序集的客户端 14、ClassLib5.dll客户端ClassLib5Clt.exe(dosapp) 执行“添加引用”选择ClassLib5.dll,尽管选择了此dll,只是用来建立本地代理,还要选择如下dll(注意版本) C:WINDOWS\Microsoft.NET\Frameworkv\1.4322\System.Runtime.Remoting.dll using System; using ClassLib5; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Http; namespace ClassLib5Clt{ class Class1 { [STAThread] static void Main(string[] args){ …}}}
远程服务程序集的客户端 14、ClassLib5.dll客户端ClassLib5Clt.exe(dosapp) namespace ClassLib5Clt{ class Class1 { [STAThread] static void Main(string[] args) { //IP:端口/程序域,类名 Type t=typeof(ClassLib5.DemoClass); //远程对象类 string url=@"tcp://127.0.0.1:8601/Remote1"; //客户方激活方式,客户端new本地代理时,服务也构造对象 RemotingConfiguration.RegisterActivatedClientType(t,url); ClassLib5.DemoClass obj=new DemoClass();//本地代理 Console.WriteLine("本地代理已创建");Console.Read(); obj.ShowAppDomain(); //执行结果显示在服务器中 obj.ShowCount(); Console.WriteLine("远程返回的count="+ obj.GetCount().ToString());Console.ReadLine();}}}
远程程序的调用过程 15、执行ClassLib5Srv.exe 再执行ClassLib5Clt.exe,可看到效果为:
远程对象的激话方式 通过ClassLib5Srv.exe与ClassLib5Clt.exe的执行可知, 当客户方建立远程对象的本地代理时,远程对象的构造函数被调用即远程对象被创建,与COM稍有不同,执行远程方法时才创建远程对象。 每创建本地代理,就创建一个远程对象,非单实例! 这种方式,称为客户方激话。 服务方注册对象方法: Type t=typeof(ClassLib5.DemoClass);//远程对象类 RemotingConfiguration.RegisterActivatedServiceType(t); 客户方调用对象的方法: Type t=typeof(ClassLib5.DemoClass);//远程对象类 string url=@"tcp://127.0.0.1:8601/Remote1";//位置RemotingConfiguration.RegisterActivatedClientType(t,url);
远程对象的激话方式 17、服务方激话-SingleCall(仅被单次调用) 客户方每调用一次服务方法,自动创建一个实例。 1、新建服务方执行环境ClassLib5SrvSingleCall.exe 添加对ClassLib5.dll的引用 添加对System.Runtime.Remoting.dll的引用 using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Http; 2、新建客户方ClassLib5CltSingleCall.exe 添加对ClassLib5.dll的引用 添加对System.Runtime.Remoting.dll的引用 using语句同执行环境。
远程服务程序集的执行环境 17、ClassLib5.dll宿主程序ClassLib5SrvSingleCall namespace ClassLib5SrvSingleCall{ class Class1 { [STAThread] static void Main(string[] args) { IChannelReceiver tcpChn1=new TcpChannel(8601); ChannelServices.RegisterChannel(tcpChn1); IChannel httpChn1=new HttpChannel(8602); ChannelServices.RegisterChannel(httpChn1); Type t=typeof(ClassLib5.DemoClass);//DC为类的别名 RemotingConfiguration.RegisterWellKnownServiceType( t,"DC",WellKnownObjectMode.SingleCall); RemotingConfiguration.ApplicationName="Remote2"; Console.WriteLine("远程对象的执行环境等待访问...."); Console.Read(); }}}
远程服务程序集的客户端 17、ClassLib5CltSingleCall.exe namespace ClassLib5CltSingleCall{ class Class1 { [STAThread] static void Main(string[] args) { Type t=typeof(ClassLib5.DemoClass); string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url); ClassLib5.DemoClass obj=new DemoClass();//创建地代 Console.WriteLine("客户方创建了本地对象"); //服务没反应 Console.ReadLine(); obj.ShowAppDomain(); obj.ShowCount(); Console.WriteLine("远程返回值"+ obj.GetCount().ToString()); Console.ReadLine(); }}}
远程程序的调用过程 17、执行ClassLib5SrvSingleCall.exe, 再执行ClassLib5CltSingleCall.exe, 客户方创建对象时,远程对象的构造函数未执行 每执行1个方法就执行1次构造函数,即构造出1对象
远程对象的激话方式 17、服务方激话-Singleton: 当客户方创建本地代理对象时,远程服务方的构造函数不调用,即远程对象不创建。 当有客户首次调用远程对象的函数时,远程对象被构造一次,节约资源!!! 服务方注册对象方法: RemotingConfiguration.ApplicationName="Remote2"; Type t=typeof(ClassLib5.DemoClass);//远程对象类 RemotingConfiguration.RegisterWellKnownServiceType( t,"DC",WellKnownObjectMode.Singleton); 客户方调用对象的方法: Type t=typeof(ClassLib5.DemoClass); string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url);
远程服务程序集的执行环境 18、ClassLib5.dll执行环境ClassLib5SrvSingleton namespace ClassLib5SrvSingleton{ //单实例 class Class1 { [STAThread] static void Main(string[] args) { IChannelReceiver tcpChn1=new TcpChannel(8601); ChannelServices.RegisterChannel(tcpChn1); IChannel httpChn1=new HttpChannel(8602); ChannelServices.RegisterChannel(httpChn1); Type t=typeof(ClassLib5.DemoClass); //注册对象 RemotingConfiguration.RegisterWellKnownServiceType( t,"DC",WellKnownObjectMode.Singleton); //仅此句不同 RemotingConfiguration.ApplicationName="Remote2"; Console.WriteLine("远程对象的执行环境等待访问...."); Console.Read(); }}}
远程服务程序集的客户端 18、ClassLib5CltSingleton.exe namespace ClassLib5CltSingleton { class Class1 { [STAThread] static void Main(string[] args) { Type t=typeof(ClassLib5.DemoClass); //类名即接口信息 string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url); ClassLib5.DemoClass obj=new DemoClass(); //t绑于url Console.WriteLine("客户方创建了本地对象"); Console.ReadLine(); obj.ShowAppDomain(); obj.ShowCount(); Console.WriteLine("远程返回值"+ obj.GetCount().ToString()); Console.ReadLine(); }}} //客户方运行多次后,服务方只有1个,Count显示被调用数
远程程序的调用过程 18、执行ClassLib5SrvSingleton.exe, 再执行ClassLib5CltSingleton.exe, 客户方创建对象时,远程对象的构造函数未执行 多个客户端只执行1个构造函数,即只有1个实例!共享也
远程对象的激话方式 18、服务方激话方式-SingleCall: 当客户方创建对象(new DemoClass()),远程服务方的构造函数不调用,即远程对象未创建。 每调用远程对象的函数一次,远程对象构造一次。 服务方注册对象方法: RemotingConfiguration.ApplicationName="Remote2"; Type t=typeof(ClassLib5.DemoClass);//远程对象类 RemotingConfiguration.RegisterWellKnownServiceType( t,"DC",WellKnownObjectMode.SingleCall); 客户方调用对象的方法-不变! Type t=typeof(ClassLib5.DemoClass); string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url);
自定义通道 19、前面各例的通道是采用TCP或HTTP,消息的格式是采用默认格式。服务方多个端口其实只要一个即可 如果通道是TCP,则消息格式为Binary二进制 如果通道是HTTP,则消息格式为SOAP。 可重新定义通道与消息格式 public TcpChannel(IDictionary properties, IClientChannelSinkProvider clientSinkProvider, IServerChannelSinkProvider serverSinkProvider); IDictionary是key,已经预先定义好了的 属性/值 集合,属性有通道名称、端口号等。 IClientChannelSinkProvider:客户端通道消息所采用的格式; IServerChannelSinkProvider:服务端通道消息所采用的格式 创建服务端消息格式clientSinkProvider=null, 创建客户端消息格式serverSinkProvider=null,
远程服务程序集的执行环境 19、ClassLib5.dll执行环境ClassLib5SrvSelfChannel 添加ClassLib5.dll,System.Runtime.Remoting.dll using System; using ClassLib5; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels.Tcp; using System.Collections;//自定义通道必须新引用此类 namespace ClassLib5SrvSelfChannel{ class Class1 { [STAThread] static void Main(string[] args){ …… } }}
远程服务程序集的执行环境 19、ClassLib5.dll执行环境ClassLib5SrvSelfChannel static void Main(string[] args){ IServerChannelSinkProvider formatter;//消息格式Binary formatter = new BinaryServerFormatterSinkProvider(); IDictionary propertyDic = new Hashtable(); propertyDic["name"] = "CustomTcp";//默认Tcp,现自定义 propertyDic["port"] = 8601;//端口号, 引用时仍是tcp!! IChannel tcpChnl = new TcpChannel( propertyDic, null, formatter);//客户端消息格式为null ChannelServices.RegisterChannel(tcpChnl); Type t=typeof(ClassLib5.DemoClass);//注册对象 RemotingConfiguration.RegisterWellKnownServiceType( t,"DC",WellKnownObjectMode.Singleton); RemotingConfiguration.ApplicationName="Remote2"; Console.WriteLine("远程执行环境");Console.Read();}
远程服务程序集的客户端 19、ClassLib5CltSelfChannel.exe //using同执行环境 namespace ClassLib5CltSingleCall{ class Class1 { [STAThread] static void Main(string[] args) { Type t=typeof(ClassLib5.DemoClass); string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url); ClassLib5.DemoClass obj=new DemoClass(); Console.WriteLine("客户创建对象"); Console.ReadLine(); obj.ShowAppDomain(); obj.ShowCount(); Console.WriteLine("远程返回值"+ obj.GetCount().ToString()); Console.ReadLine();}}}
客户端不注册直接创建对象 20、前面各例的客户端,采用如下语句注册远程对象 Type t=typeof(ClassLib5.DemoClass);//远程对象类 string url=@"tcp://127.0.0.1:8601/Remote1";//位置 RemotingConfiguration.RegisterActivatedClientType(t,url); 或 string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url); 然后使用如下语句构造远程对象的本地代理: ClassLib5.DemoClass obj=new DemoClass(); 能否不注册直接构造远程对象呢?可以! 但只能服务激活对象,且创建的对象只能有无参数的构造函数,并且在获得对象的同时创建代理。很少使用! RemotingServices.Connect(t,url) as DemoClass、 Activator.GetObject(t,url) as DemoClass、 Activator.CreateInstance(t,null,new UrlAttribute(url)) as
下载远程对象到客户端(一) 21、前面各例中,为了提高系统的运行效率,远程对象都在服务端的执行环境即宿主程序域中执行,仅通过传引用封送(Marshal By Reference)在客户端建立代理。 有时出于某种需求,希望远程对象的方法或函数在客户端执行,如待处理的文件在客户端,这时需要传值封送(Marshal By Value). 软件按次使用按次付费!云计算不就是这种模式吗? 为此建立ClassLib6.dll,它包含ClassLib5.dll中类DemoClass,还新建类ExeClass,新类中执行一个EXE文件,完成对cs源程序的编译。 新建ClassLib6Srv.exe与ClassLib6Client.exe, 对ClassLib6.dll与System.Runtime.Remoting.dll添加引用。
远程服务程序集 21、新建ClassLib6.dll,添加DemoClass、ExeClass using System; namespace ClassLib6{ public class DemoClass:MarshalByRefObject { public DemoClass() { Console.WriteLine(" 类DemoClass的对象已建立"); } public void ShowAppDomain() { AppDomain cd = AppDomain.CurrentDomain; Console.WriteLine("DemoClass的对象所在域="+ cd.FriendlyName);} public ExeClass getExeClass(){ return new ExeClass();} }}
远程服务程序集 21、新建ClassLib6.dll,添加DemoClass、ExeClass using System; using System.Diagnostics;//因为要执行DOS命令 namespace ClassLib6{ [Serializable] public class ExeClass{ public ExeClass(){ Console.WriteLine("类ExeClass的对象已创建");} public bool CompileCSharp(string srcFileName){ bool isOk=true; try{ //…… } catch { isOk=false; } return isOk;}}} //每个类的一个cs文件
远程服务程序集 21、新建ClassLib6.dll,添加DemoClass、ExeClass public bool CompileCSharp(string srcFileName){ bool isOk=true; try{ ProcessStartInfo info=new ProcessStartInfo(); info.FileName = "cmd.exe"; info.UseShellExecute =false; info.RedirectStandardInput =true; info.RedirectStandardOutput =true; info.RedirectStandardError = true; info.CreateNoWindow = true; string cmdStr = "csc "+srcFileName; Process proc=Process.Start(info); proc.StandardInput.WriteLine(cmdStr); proc.StandardInput.WriteLine("exit"); AppDomain cd=AppDomain.CurrentDomain; Console.WriteLine(" 类ExeClass域:"+cd.FriendlyName);} catch {isOk=false; } return isOk; }}}
远程服务程序集的执行环境 21)ClassLib6.dll执行环境ClassLib6Srv.exe (using略) namespace ClassLib6Srv{ class Class1{ [STAThread] static void Main(string[] args) { IChannelReceiver tcpChn1=new TcpChannel(8601); ChannelServices.RegisterChannel(tcpChn1);//通道 Type t=typeof(ClassLib6.DemoClass); RemotingConfiguration.RegisterWellKnownServiceType( t,"DC",WellKnownObjectMode.Singleton);//类名 //定义程序域名 RemotingConfiguration.ApplicationName="Remote2"; Console.WriteLine("远程执行环境已启动...."); Console.Read(); }}}
远程服务程序集的客户端 21)ClassLib6.dll的ClassLib6Client.exe (using略) namespace ClassLib6Client{ class Class1{ //将源程序临时下发客户端内存或磁盘 [STAThread] //编译完后将其源程序删除 static void Main(string[] args){ //注册对象-- 协议://IP:port/程序域/对象名 Type t=typeof(ClassLib6.DemoClass); string url=@"tcp://127.0.0.1:8601/Remote2/DC"; RemotingConfiguration.RegisterWellKnownClientType(t,url); ClassLib6.DemoClass obj=new DemoClass(); obj.ShowAppDomain();//传引送封送对象的域在服务端 ClassLib6.ExeClass obj2=obj.getExeClass(); bool isOk=obj2.CompileCSharp("test.cs");//编译在客户端 Console.WriteLine(" 编译结果:"+isOk.ToString()); }}} //因为不可能将所有类库安装在客户端。