按引用方式开发的Remoting实例
(使用继承自MarshalByRefObject的类的方法)。
(DEMO:RemotingExam_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,当然,也可以传递一个复杂的类型,如:ArrayList、DataSet等可序列化的类型。当然,也可以传递你自定义的类型。
下面,我们通过一个实例,讲解如何传递可序列化的数据类型。