使用C#下的Remoting有一段时间了,感觉开始的时候理解比较困难,不过了解了其工作模式后也相对觉得容易多了,现在将一些心得体会记录一下。
Remoting原理:Remoting实际上就是在服务器与客户端之间利用一个对象(RemoteObject)进行通讯,服务器端产生这个对象,客户端远程获取这个对象并利用对象的属性和方法操作服务器端的数据。Remoting的通讯协议可以是IPC、HTTP或者TCP。从性能上讲,IPC信道的通讯性能最佳,但客户端服务器端只能存在于同一个域中;HTTP性能最差,但是HTTP协议几乎能穿越所有的防火墙,所以在远程访问中使用HTTP的情况较多;TCP性能介于前两者之间。
Remoting模式的通讯省去了编写大量通讯协议的过程,将数据与方法用面向对象的方式进行的封装,大大加快了开发进度。
下面,将举例开发一对简单的Remoting通讯的例子程序。
第一步:创建公共对象类(CommProxyClass),公共对象类必须继承于MarshalByRefObject;
public class CommProxyClass:MarshalByRefObject{};
第二步:为这个公共对象添加一个方法:
public int GetProcessCount()
{
return Environment.ProcessorCount;
}
注:公共对象的方法编写视对象位置而定。如果对象由服务器端程序集提供,对象内部可以直接访问服务器端数据,那么方法内部可以直接返回服务器端数据;如果这个对象存在于一个独立于客户端和服务器端之外的程序集,那么对象应该提供访问服务器端对象的方法代理(Delegate),服务器端必须实现这个代理以实现方法:
///获取服务器操作系统环境变量-处理器数量的代理方法
public delegate int GetProcessCountDelegate();
///实现获取服务器处理器数量的代理对象
public static GetProcessCountDelegate GetProcessCountHandle;
///获取服务器处理器数量
public int GetProcessCount()
{
if(GetProcessCountHandle != null)
return GetProcessCountHandle();
else
return 0;//返回0,说明服务器没有实现方法
}
第三步:服务器端代码。如果公共对象不由服务器端程序集提供,那么首先需要引用公共对象程序集。
添加如下引用:
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Serialization.Formatters.Binary;//实现二进制序列化
实现以上引用需要在解决方案管理器中添加using System.Runtime.Remoting引用,方法是在解决方案管理器中的项目名称上点右键->添加引用->.Net下选择System.Runtime.Remoting
再添加队公共对象程序集的引用
using UserServerClient;//假定UserServerClient是公共对象程序集的命名空间;
class Program
{
static void Main(string[] args)
{
//注册通道
RegisterChannel();
//方法实现
CommProxyClass.GetProcessCountHandle = new GetProcessCountDelegate (GetProcessCount);
Console.ReadLine();
}
}
在上面的类中添加方法
/// <summary>
/// 注册通道
/// </summary>
private static void RegisterChannel()
{
//注册对象,typeof的参数就是我们要实现的公共对象的类名称,其他参数请Google一下
RemotingConfiguration.RegisterWellKnownServiceType(typeof(CommProxyClass), "RemotingServer", WellKnownObjectMode.Singleton);
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();//对象以二进制序列化方式进行序列化
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
/*由于从.NET Framework 1.1起,缺省情况下DelegateSerializationHolder不允许被反序列化,(即FormatterSink.TypeFilterLevel = TypeFilterLevel.low)。为了实现远程事件处理,我们必须解除该约束,使ServerFormatterSink.TypeFilterLevel = TypeFilterLevel.Full */
IDictionary props = new Hashtable();
// 当server调用client的时候一定要为client定义channel
props["port"] = 1234;//服务器端监听端口为1234
props["name"] = "RemotingServer";//通道名称,请指定为一个唯一的,以免服务器端实现多个Remoting对象时候发生冲突
TcpChannel chanle = new TcpChannel(props, null, provider);
try
{
ChannelServices.RegisterChannel(chanle, false);//C#2.0版本
ChannelServices.RegisterChannel(chanle);//C#1.1版本
}
catch (RemotingException)
{
Console.WriteLine("错误:无法创建Remoting通道");
}
}
private static int GetProcessCount()
{
return Environment.ProcessorCount;
}
好了,服务端代码就算完成了
第四步:客户端代码
添加如下引用:
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Serialization.Formatters.Binary;//实现二进制序列化
再添加队公共对象程序集的引用:
using UserServerClient;//假定UserServerClient是公共对象程序集的命名空间;
class Program
{
static void Main(string[] args)
{
//链接到服务器
ConnentToServer();
//方法实现
int mProcessCount =_ServerProxy.GetProcessCount();
Console.WriteLine(String.Format("服务器处理器数量:{0}",mProcessCount ));
Console.ReadLine();
}
}
在上面的类中添加一个对象定义
private static CommProxyClass _ServerProxy = null;
在上面的类中添加方法
/// <summary>
/// 链接到服务器
/// </summary>
private static void ConnentToServer()
{
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
IDictionary props = new Hashtable();
// 当server调用client的时候一定要为client定义channel
props["port"] = 0;
TcpChannel chanle = new TcpChannel(props, null, provider);
try
{
IChannel[] chanles = ChannelServices.RegisteredChannels;
bool mIsRigister = false;
foreach (IChannel ch in chanles)
{
if (ch.ChannelName == chanle.ChannelName)
{
mIsRigister = true;
break;
}
}
if (!mIsRigister)
ChannelServices.RegisterChannel(chanle, false);//注册通道
}
catch (RemotingException re)
{
Console.WriteLine("错误:" + re.ToString());
}
// now create a transparent proxy to the server component
_ServerProxy = (CommProxyClass)Activator.GetObject(typeof(CommProxyClass), String.Format("tcp://{0}:{1}/RemotingServer", "192.168.0.111", 1234));
//192.168.0.111是服务器端程序运行服务器的IP,1234是服务器端程序监听端口 }
OK,就这样,很简单,呵呵。
评论