Microsoft .Net Remoting系列专题之一:.Net Remoting基础篇

基于STM32设计的智能井盖(Lora组网+4G上云)_423 设计并实现了一种基于 STM32 与 Lora 通信技术的智能井盖监测系统。系统采用 STM32F103C8T6 作为核心控制器,在井盖节点端集成水位检测、有害气体检测、倾斜角度检测以及 GPS 定位等功能模块,对井盖内部环境状态和姿态变化进行实时监测。各井盖节点通过 Lora 无线通信方式组成网络,将采集的数据发送至主机(网关)进行汇聚处理。 阅读详情

原文地址:http://www.cnblogs.com/wayfarer/archive/2004/07/30/28723.html

{
                name = pName;
                sex = pSex;
                age = pAge;
            }

那么实现的代码是:
            object[] attrs = {new UrlAttribute("tcp://localhost:8080/ServiceMessage")};
            object[] objs = new object[3];
            objs[0] = "wayfarer";
            objs[1] = "male";
            objs[2] = 28;
            ServerRemoteObject.ServerObject = Activator.CreateInstance(
                typeof(ServerRemoteObject.ServerObject),objs,attrs);
可以看到,objs[]数组传递的就是构造函数的参数。

b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);

参数说明:
assemblyName :将在其中查找名为 typeName 的类型的程序集的名称。如果 assemblyName 为空引用(Visual Basic 中为 Nothing),则搜索正在执行的程序集。
typeName:首选类型的名称。
activationAttributes :包含一个或多个可以参与激活的属性的数组。

参数说明一目了然。注意这个方法返回值为ObjectHandle类型,因此代码与前不同:
            object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};          
            ObjectHandle handle = Activator.CreateInstance("ServerRemoteObject",
                                   "ServerRemoteObject.ServerObject",attrs);
            ServerRemoteObject.ServerObject obj = (ServerRemoteObject.ServerObject)handle.Unwrap();

这个方法实际上是调用的默认构造函数。ObjectHandle.Unwrap()方法是返回被包装的对象。

说明:要使用UrlAttribute,还需要在命名空间中添加:using System.Runtime.Remoting.Activation;

五、Remoting基础的补充

通过上面的描述,基本上已经完成了一个最简单的Remoting程序。这是一个标准的创建Remoting程序的方法,但在实际开发过程中,我们遇到的情况也许千奇百怪,如果只掌握一种所谓的“标准”,就妄想可以“一招鲜、吃遍天”,是不可能的。

1、注册多个通道

在Remoting中,允许同时创建多个通道,即根据不同的端口创建不同的通道。但是,Remoting要求通道的名字必须不同,因为它要用来作为通道的唯一标识符。虽然IChannel有ChannelName属性,但这个属性是只读的。因此前面所述的创建通道的方法无法实现同时注册多个通道的要求。

这个时候,我们必须用到System.Collection中的IDictionary接口:

注册Tcp通道:
IDictionary tcpProp = new Hashtable();
tcpProp["name"] = "tcp9090";
tcpProp["port"] = 9090;
IChannel channel = new TcpChannel(tcpProp,
new BinaryClientFormatterSinkProvider(),
new BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);

注册Http通道:
IDictionary httpProp = new Hashtable();
httpProp["name"] = "http8080";
httpProp["port"] = 8080;
IChannel channel = new HttpChannel(httpProp,
new SoapClientFormatterSinkProvider(),
new SoapServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);

在name属性中,定义不同的通道名称就可以了。

2、远程对象元数据相关性

由于服务器端和客户端都要用到远程对象,通常的方式是生成两份完全相同的对象Dll,分别添加引用。不过为了代码的安全性,且降低客户端对远程对象元数据的相关性,我们有必要对这种方式进行改动。即在服务器端实现远程对象,而在客户端则删除这些实现的元数据。

由于激活模式的不同,在客户端创建对象的方法也不同,所以要分离元数据的相关性,也应分为两种情况。

(1) WellKnown激活模式:

通过接口来实现。在服务器端,提供接口和具体类的实现,而在客户端仅提供接口:
    public interface IServerObject
    {
        Person GetPersonInfo(string name,string sex,int age);
    }

public class ServerObject:MarshalByRefObject,IServerObject
{ ......}
注意:两边生成该对象程序集的名字必须相同,严格地说,是命名空间的名字必须相同。
          
(2) 客户端激活模式:

如前所述,对于客户端激活模式,不管是使用静态方法,还是使用CreateInstance()方法,都必须在客户端调用构造函数实例化对象。所以,在客户端我们提供的远程对象,就不能只提供接口,而没有类的实现。实际上,要做到与远程对象元数据的分离,可以由两种方法供选择:

a、利用WellKnown激活模式模拟客户端激活模式:

方法是利用设计模式中的“抽象工厂”,下面的类图表描述了总体解决方案:

我们在服务器端的远程对象中加上抽象工厂的接口和实现类:
    public interface IServerObject
    {
        Person GetPersonInfo(string name,string sex,int age);
    }

    public interface IServerObjFactory
    {
        IServerObject CreateInstance();      
    }

    public class ServerObject:MarshalByRefObject,IServerObject
    {
        public Person GetPersonInfo(string name,string sex,int age)
        {
            Person person = new Person();
            person.Name = name;
            person.Sex = sex;
            person.Age = age;
            return person;
        }      
    }

    public class ServerObjFactory:MarshalByRefObject,IServerObjFactory
    {
        public IServerObject CreateInstance()
        {
            return new ServerObject();
        }
    }

然后再客户端的远程对象中只提供工厂接口和原来的对象接口:
    public interface IServerObject
    {
        Person GetPersonInfo(string name,string sex,int age);
    }

    public interface IServerObjFactory
    {
        IServerObject CreateInstance();      
    }
我们用WellKnown激活模式注册远程对象,在服务器端:
           //传递对象;
            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof(ServerRemoteObject.ServerObjFactory),
                "ServiceMessage",WellKnownObjectMode.SingleCall);

注意这里注册的不是ServerObject类对象,而是ServerObjFactory类对象。

客户端:
ServerRemoteObject.IServerObjFactory serverFactory =              
                (ServerRemoteObject.IServerObjFactory) Activator.GetObject(
                typeof(ServerRemoteObject.IServerObjFactory),
                "tcp://localhost:8080/ServiceMessage");

ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();

为什么说这是一种客户端激活模式的模拟呢?从激活的方法来看,我们是使用了SingleCall模式来激活对象,但此时激活的并非我们要传递的远程对象,而是工厂对象。如果客户端要创建远程对象,还应该通过工厂对象的CreateInstance()方法来获得。而这个方法正是在客户端调用的。因此它的实现方式就等同于客户端激活模式。

b、利用替代类来取代远程对象的元数据

实际上,我们可以用一个trick,来欺骗Remoting。这里所说的替代类就是这个trick了。既然是提供服务,Remoting传递的远程对象其实现的细节当然是放在服务器端。而要在客户端放对象的副本,不过是因为客户端必须调用构造函数,而采取的无奈之举。既然具体的实现是在服务器端,又为了能在客户端实例化,那么在客户端就实现这些好了。至于实现的细节,就不用管了。

如果远程对象有方法,服务器端则提供方法实现,而客户端就提供这个方法就OK了,至于里面的实现,你可以是抛出一个异常,或者return 一个null值;如果方法返回void,那么里面可以是空。关键是这个客户端类对象要有这个方法。这个方法的实现,其实和方法的声明差不多,所以我说是一个trick。方法如是,构造函数也如此。

还是用代码来说明这种“阴谋”,更直观:

服务器端:
    public class ServerObject:MarshalByRefObject
    {
        public ServerObject()
        {
          
        }

        public Person GetPersonInfo(string name,string sex,int age)
        {
            Person person = new Person();
            person.Name = name;
            person.Sex = sex;
            person.Age = age;
            return person;
        }      
    }

客户端:
    public class ServerObject:MarshalByRefObject
    {
        public ServerObj()
        {
            throw new System.NotImplementedException();
        }

        public Person GetPersonInfo(string name,string sex,int age)
        {
            throw new System.NotImplementedException();
        }      
    }

比较客户端和服务器端,客户端的方法GetPersonInfo(),没有具体的实现细节,只是抛出了一个异常。或者直接写上语句return null,照样OK。我们称客户端的这个类为远程对象的替代类。

3、利用配置文件实现

前面所述的方法,于服务器uri、端口、以及激活模式的设置是用代码来完成的。其实我们也可以用配置文件来设置。这样做有个好处,因为这个配置文件是Xml文档。如果需要改变端口或其他,我们就不需要修改程序,并重新编译,而是只需要改变这个配置文件即可。

(1) 服务器端的配置文件:
<configuration>
  <system.runtime.remoting>
    <application name="ServerRemoting">
      <service>
        <wellknown mode="Singleton" type="ServerRemoteObject.ServerObject" objectUri="ServiceMessage"/>
      </service>
      <channels>
         <channel ref="tcp" port="8080"/>
      </channels>
    </application>
  </system.runtime.remoting>
</configuration>

如果是客户端激活模式,则把wellknown改为activated,同时删除mode属性。

把该配置文件放到服务器程序的应用程序文件夹中,命名为ServerRemoting.config。那么前面的服务器端程序直接用这条语句即可:
RemotingConfiguration.Configure("ServerRemoting.config");

(2) 客户端配置文件

如果是客户端激活模式,修改和上面一样。调用也是使用RemotingConfiguration.Configure()方法来调用存储在客户端的配置文件。

配置文件还可以放在machine.config中。如果客户端程序是web应用程序,则可以放在web.config中。

4、启动/关闭指定远程对象

Remoting中没有提供类似UnregisterWellKnownServiceType()的方法,也即是说,一旦通过注册了远程对象,如果没有关闭通道的话,该对象就一直存在于通道中。只要客户端激活该对象,就会创建对象实例。如果Remoting传送的只有一个远程对象,这不存在问题,关闭通道就可以了。如果传送多个远程对象呢?要关闭指定的远程对象应该怎么做?关闭之后又需要启动又该如何?

我们注意到在Remoting中提供了Marshal()和Disconnect()方法,答案就在这里。Marshal()方法是将MarshalByRefObject类对象转化为ObjRef类对象,这个对象是存储生成代理以与远程对象通讯所需的所有相关信息。这样就可以将该实例序列化以便在应用程序域之间以及通过网络进行传输,客户端就可以调用了。而Disconnect()方法则将具体的实例对象从通道中断开。

方法如下:
首先注册通道:
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);

接着启动服务:
先在服务器端实例化远程对象。
ServerObject obj = new ServerObject();

然后,注册该对象。注意这里不用RemotingConfiguration.RegisterWellKnownServiceType(),而是使用RemotingServices.Marshal():

ObjRef objrefWellKnown = RemotingServices.Marshal(obj, "ServiceMessage");

如果要注销对象,则:
RemotingServices.Disconnect(obj);

要注意,这里Disconnect的类对象必须是前面实例化的对象。正因为此,我们可以根据需要创建指定的远程对象,而关闭时,则Disconnect之前实例化的对象。

至于客户端的调用,和前面WellKnown模式的方法相同,仍然是通过Activator.GetObject()来获得。但从实现代码来看,我们会注意到一个问题,由于服务器端是显式的实例化了远程对象,因此不管客户端有多少,是否相同,它们调用的都是同一个远程对象。因此我们将这个方法称为模拟的SingleTon模式。

客户端激活模式

我们也可以通过Marshal()和Disconnect()来模拟客户端激活模式。首先我们来回顾“远程对象元数据相关性”一节,在这一节中,我说到采用设计模式的“抽象工厂”来创建对象实例,以此用SingleCall模式来模拟客户端激活模式。在仔细想想前面的模拟的SingleTon模式。是不是答案就将呼之欲出呢?

在“模拟的SingleTon”模式中,我们是将具体的远程对象实例进行Marshal,以此让客户端获得该对象的引用信息。那么我们换一种思路,当我们用抽象工厂提供接口,工厂类实现创建远程对象的方法。然后我们在服务器端创建工厂类实例。再将这个工厂类实例进行Marshal。而客户端获取对象时,不是获取具体的远程对象,而是获取具体的工厂类对象。然后再调用CreateInstance()方法来创建具体的远程对象实例。此时,对于多个客户端而言,调用的是同一个工厂类对象;然而远程对象是在各个客户端自己创建的,因此对于远程对象而言,则是由客户端激活,创建的是不同对象了。

当我们要启动/关闭指定对象时,只需要用Disconnet()方法来注销工厂类对象就可以了。

六、小结

Microsoft.Net Remoting真可以说是博大精深。整个Remoting的内容不是我这一篇小文所能尽述的,更不是我这个Remoting的初学者所能掌握的。王国维在《人间词话》一书中写到:古今之成大事业大学问者,必经过三种境界。“昨夜西风凋碧树,独上高楼,望尽天涯路。”此第一境界也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界也。“众里寻他千百度,蓦然回首,那人却在灯火阑珊处。”此第三境界也。如以此来形容我对Remoting的学习,还处于“独上高楼,望尽天涯路”的时候,真可以说还未曾登堂入室。

或许需得“衣带渐宽”,学得Remoting“终不悔”,方才可以“蓦然回首”吧。

 

PCL 基于超体素的点云分割【2024最新版】 点云超体素分割原理介绍、代码实现以及如何做到将每一个超体素内的点进行单独保存而不造成点云缺失的方法。博客长期更新,本文最近一次更新时间为:2024年12月29日。 阅读详情

相关推荐

(详细教程)opencv+pycharm+笔记本摄像头 做一个人脸识别

文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 一、安装opencv-python 这里推荐使用pycharm直接安装opencv-python 打开文件——设置——python解释器——点击+号 搜索opencv-python,点击安装包,等待片刻即可安装好 检查是否安装好,新建一个项目输入以下代码,若无报错则安装好 import cv2 二、准备分类器 咱们使用opencv官方提供的分类器,包括面部识别,侧...

qq_52852432的博客 1万+

Microsoft .Net Remoting系列专题之一:Net Remoting基础篇

Microsoft .Net Remoting系列专题之一:Net Remoting基础篇一、Remoting基础什么是Remoting,简而言之,我们可以将其看作是一种分布式处理方式。从微软的产品角度来看,可以说Remoting就是DCOM的一种升级,它改善了很多功能,并极好的融合到.Net平台下。Microsoft® .NET Remoting 提供了一种允

凌铖的专栏 362

SIPVicious实战指南:从零开始掌握VoIP渗透测试(Kali Linux环境)

本文为SIPVicious实战指南,详细介绍了在Kali Linux环境下进行VoIP渗透测试的完整流程。从环境准备与工具安装开始,逐步讲解如何使用svmap扫描SIP服务器、svwar枚举PBX分机号,以及svcrack进行密码安全测试,并整合svreport生成报告。旨在帮助安全从业者从零开始,系统掌握这套经典开源工具,在授权范围内有效评估VoIP系统的安全性。

weixin_29327977的博客 540

Microsoft .Net Remoting系列专题之一:Net Remoting基础篇

Microsoft .Net Remoting系列专题之一一、Remoting基础什么是Remoting,简而言之,我们可以将其看作是一种分布式处理方式。从微软的产品角度来看,可以说Remoting就是DCOM的一种升级,它改善了很多功能,并极好的融合到.Net平台下。Microsoft® .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这也正是我们使

lovemexiaoyu的专栏 553

无迹卡尔曼滤波(UKF)推导

提出,使用了一种不同的方法来计算误差协方差矩阵。它不使用(连续时间)黎卡提方程(Riccati Equations,形如。的一类非线性方程)或(离散时间)协方差传播和更新规律。关键的思想是基于一组特殊的样本点来估计误差协方差,这些点被称为。无迹卡尔曼滤波(Unscented Kalman Filter),最初由。点(Sigma Points),这些点直接通过原始非线性模型传播。无迹变换表示有三个特殊样本点的高斯分布,称为。

生活如此美好,致敬每一个热爱“热爱”的人! 1778

【推荐100个unity插件】将UE的资源导出到unity使用—— Exporter for Unreal to/for Unity插件

Unreal to Unity导出器是一款帮助开发者将Unreal Engine项目转换为Unity的工具。文章详细介绍了使用步骤:1)安装虚幻引擎并创建项目;2)在Plugins文件夹中安装导出插件;3)导入UE资产到Content文件夹;4)配置导出参数并完成转换。文末推荐了多个Unity开发专栏,涵盖C#编程、2D/3D开发、特效制作等内容。作者向宇提供了博客地址,欢迎读者交流学习。该工具能有效提升项目迁移效率,适合需要跨引擎开发的用户使用。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 852
上一篇: VS调试错误:若要调试此模块,请将其项目生成配置更改为"调试"模式
下一篇: 一步一步学Remoting系列文章
ArvinStudy
博客等级 码龄15年 83粉丝 193原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值