按引用方式开发的Remoting实例

时间:2022-08-30 13:57:35

按引用方式开发的Remoting实例

(使用继承自MarshalByRefObject的类的方法)。

(DEMORemotingExam_1(2005))

 

(一)   开发Remoting的基本步骤:

 

开发一个Remoting的应用可以分为三个步骤:

第一步、创建远程对象,也就是可供远程和客户端调用的共用的对象。

第二步、在远程创建一个应用程序,做为“宿主”程序,用这个程序来监听,以接收客户端的请求。

第三步、创建一个客户端程序,以调用远程的对象。

由于在“宿主”程序和客户端都要使用远程对象的类,所以,这两个项目中,都要添加“远程对象项目”的项目引用。

(二)一个最简单的Remoting的实例

 

下面,我们就按这个步骤来开发一个Remoting的实例。

第一步、创建远程对象。

由于我们采用按引用传递的方式,所以,我们的类要从MarshalByRefObject继承下来。只有Public的类才能被远程调用。

namespace RemotingExam_1_Pub

{

    public class Pub_Class : MarshalByRefObject

    {

        public string PubName = "这是远程对象的属性";

        public String PubClassMethod(String name)

        {

            return "这是公用类PubClassthod方法的返回串,你的名字叫" + name;

        }

 

 

    }

 

 

    public class Class_Temp //非远程可访问的类

    {

       

        public String PubClassthod(String name)

        {

            return "这是非MarshalByRefObject继承的类";

        }

 

 

    }

 

}

 

第二步、创建一个远程宿主程序:

远程宿主程序可以是一个控制台程序,也可以是一个WinForm程序,也可以是一个WEB服务,放在IIS中,让IIS作为你的远程对象的宿主程序。

一般情况下采用Windows Service,这样,服务器一旦起动,程序就起来了,不用人工的参与了。(关于Windows Service的开发,我们也曾讲过一次课。)

 

创建宿主程序的步骤:

1、定义并注册通道:这个通道可以是内置的TCP通道或者HTTP通道,也可以是你自己编写的通道。

2、注册服务器激活的远程对象。

 

注册远程对象需要完成下面的工作:

首先要知道你的远程对象是单一实例的(Singleton)还是单一调用的(SingleCall),这个有点像以前DELPHI三层框架下的服务端的运行方式。还要知道的就是远程对象的URL,也就是客户端如果通过网络访问远程对象,要给它一个地址。

部分源代码如下:

//定义两个通道,一个TCP的一个HTTP的,使用不同的端口号

        TcpChannel chan1 = new TcpChannel(1000);

        HttpChannel chan2 = new HttpChannel(1001);

 

        //起动服务 按钮

        private void BtnStart_Click(object sender, EventArgs e)

        {

            ChannelServices.RegisterChannel(chan1, false);

            ChannelServices.RegisterChannel(chan2, false);

            System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType

                (typeof(RemotingExam_1_Pub.Pub_Class), "MyServer", System.Runtime.Remoting.WellKnownObjectMode.Singleton);

            this.textBox1.AppendText("服务启动,按“停止”键退出服务端的监听..."+(char)13+(char)10);//.listView1.Items.Add("服务启动,按任意键退出服务端的监听...");

        this.textBox1.AppendText("TCP端口号:5000");

           this.textBox1.AppendText("HTTP端口号:5001");

         

        }

        //停止服务 按钮

        private void btnStop_Click(object sender, EventArgs e)

        {

            ChannelServices.UnregisterChannel(chan1);//最后注销通道

            ChannelServices.UnregisterChannel(chan2);

            this.textBox1.AppendText("服务启动,服务已停止..." + (char)13 + (char)10);

 

            // this.listView1.Items.Add("服务已停止。");

        }

 

代码说明:

我们在这里创建了一个WinForm的应用程序。

首先定义两个通道,一个TCP的一个HTTP的,使用不同的端口号

  TcpChannel chan1 = new TcpChannel(5000);           

HttpChannel chan2 = new HttpChannel(5001);

并注册这两个通道:

ChannelServices.RegisterChannel(chan1,false);

ChannelServices.RegisterChannel(chan2,false );

注册通道,注册后,服务就可以监听这两个通道(1000,1001)了。

然后,使用System.Runtime.Remoting.RemotingConfiguration命名空间下的方法RegisterWellKnownServiceType来把远程对象注册到服务器上。

System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType

                (typeof(RemotingPub.PubClass1), "MyServer", System.Runtime.Remoting.WellKnownObjectMode.Singleton);

这个方法有三个参数:

一是注册的对象类型,在这里是 RemotingPub.PubClass1类型,第二个参数是一个字符串“MyServer”, 这里,你可以给一个任意的名字,这是用于客户端调用时要使用名字。第三个参数是服务端激活的方式,在这里,使用Singleton(单实例方式),这种激活方式叫服务器端激活,又叫做WellKnow方式(服务器激活方式)。

服务器激活方式有两种:单实例和单调用。

别忘了服务端退出时,注销通道:

ChannelServices.UnregisterChannel(chan1);//最后注销通道

ChannelServices.UnregisterChannel(chan2);

 

注意:

由于服务端要注册对象的类型是PubClass1中的类型,所以,要添加公用类的项目引用。

还要在解决方案管理器中添加引用:System.Runtime.Remoting

然后,在代码最前面添加using如下:

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;//必须添加引用:System.Runtime.Remoting

using System.Runtime.Remoting.Channels.Http;//必须添加引用:System.Runtime.Remoting

编译后,生成一个EXE文件:Server.exe,运行程序。

 

 

 

第三步:建立客户端程序。

也可分为下面和几步:

1、注册通道:这个通道要和服务器的通道一样,如果服务器采用TCP通道,那么客户端也得采用TCP通道。

2、根据URL得到对象的代理。

3、使用代理调用远程对象。客户端对远程对象的调用都是通过这个代理来实现的,这个代理中,有和服务器一样的方法和属性。调用代理的方法就可以了,你可以把远程对象当成本地对象来调用。

 

部分源代码如下 :

调用远程对象的属性

private void button4_Click(object sender, EventArgs e)

        {

            //使用TCP通道

            TcpChannel chan1 = new TcpChannel();

            ChannelServices.RegisterChannel(chan1, false);

            //使用TCP通道得到远程对象

            RemotingExam_1_Pub.Pub_Class obj1 = (RemotingExam_1_Pub.Pub_Class)Activator.GetObject(

                typeof(RemotingExam_1_Pub.Pub_Class), "tcp://localhost:5000/MyServer");

 

            if (obj1 == null)

            {

                MessageBox.Show("不能定位到TCP服务");

            }

            else

            {

                this.textBox2.Text = obj1.PubName;

              

            }

            ChannelServices.UnregisterChannel(chan1);//最后注销通道

        }

 

 

代码说明:

首先使用TcpChannel chan1 = new TcpChannel();定义一个通道,再注册这个通道。ChannelServices.RegisterChannel(chan1,false);

然后,通过Activator对象的GetObject方法,获取远程对象的一个代理,或者说是一个副本:obj1。这样,就可以通过这个代理,由于这个代理的对象和原来的对象有相同的方法和属性,通过这个代理,就可以访问对象中的属性和方法了。

RemotingPub.PubClass1 obj1 = (RemotingPub.PubClass1)Activator.GetObject(

                typeof(RemotingPub.PubClass1), "tcp://localhost:1000/MyServer");

 

这个方法有两个参数:一是对象类型,另一个是远程服务器的URL。因为是通过TCP通道的,所以,URL要用TCP开头。

 

最后,通过代理类,调用其中的属性:this.textBox2.Text = obj1.PubName;

this.textBox2.Text = obj1.PubClassthod(this.textBox1.Text);

 

调用远程对象的方法:

使用TCP通道得到远程对象:

 

private void button1_Click(object sender, EventArgs e)

        {

            //使用TCP通道

            TcpChannel chan1 = new TcpChannel();

            ChannelServices.RegisterChannel(chan1, false);

            //使用TCP通道得到远程对象

            RemotingExam_1_Pub.Pub_Class obj1 = (RemotingExam_1_Pub.Pub_Class)Activator.GetObject(

                typeof(RemotingExam_1_Pub.Pub_Class), "tcp://localhost:1000/MyServer");

 

            if (obj1 == null)

            {

                MessageBox.Show("不能定位到TCP服务");

 

            }

            else

            {

                this.textBox2.Text = obj1.PubClassMethod(this.textBox1.Text);

                System.Console.WriteLine(

                    "Could not locate TCP server");

            }

            ChannelServices.UnregisterChannel(chan1);//最后注销通道

        }

 

 

在最后,要注销通道:ChannelServices.UnregisterChannel(chan1);

注意:

由于要定义一个RemotingPub.PubClass1类型的代理对象obj1,所以,要添加项目引用,然后还要添加System.Runtime.Remoting的引用。

 

下面是通过HTTP通道获取远程对象的代理,然后访问其中的方法:

只需要把TCP通道中的代码TcpChannel chan1 = new TcpChannel();换成HttpChannel chan1 = new HttpChannel();

然后在得到远程对象的代理时采用:

  //使用HTTP通道得到远程对象

            RemotingPub.PubClass1 obj1 = (RemotingPub.PubClass1)Activator.GetObject(

                typeof(RemotingPub.PubClass1), "Http://localhost:1001/MyServer");

就行了。其它的不用变化。

 

到这里,我们就完成了一个完整的Remoting的建立和调用过程。

 

下面运行我们的Remoting.

先启动服务端程序,然后启动客户端程序。点击按钮,就可以调用远程的方法,将参数传递到远程,并将远程方法的结果返回给客户端。

 

总结起来,就是三个步骤:创建一个服务端和客户共用的类PubClass1,然后,建立服务端,在服务端注册TCP通道或者HTTP通道,把要在远程访问的对象注册到服务上,然后建立客户端程序,在客户端中使用和服务端一样的通道,通过该通道,获取一个远程对象的代理,通过这个代理,访问远程对象中的属性和方法。

当然,退出时,别忘了注销你曾经注册过的通道就行了。

 

从这里可以看出,使用Remoting进行远程对象访问的方法,还是比较容易的。不像DCOM的调试部署那么复杂。

上面讲的是一个通过继承自MarshalByRefObject的类,实现远程对象的访问。

上面我们传递的数据类型是一个简单的类型string,当然,也可以传递一个复杂的类型,如:ArrayListDataSet等可序列化的类型。当然,也可以传递你自定义的类型。

下面,我们通过一个实例,讲解如何传递可序列化的数据类型。