.NET简谈组件程序设计之(详解NetRemoting结构)

在本人的上一篇文章中只是简单的介绍了一下.NETRemoting的一般概念和基本的使用。这篇文章我想通过自己的学习和理解将对.NETRemoting的整体的一个框架进行通俗的讲解,其中最重要的就是信道(管道)处理模型思想,这里面蕴含了很多的设计原理。[王清培版权所有,转载请给出署名]
.NETRemoting远程处理架构是一个半成品,是.NET给我们的扩展框架,要想用于商业项目必须进行一些安全、性能方面的控制。要想进行一定深度的扩展那就要必须了解它的整体结构,各个点之间的关系才能很好的控制它。

网上讲解.NETRemoting的文章很多,但是通俗易懂的没有几篇,都是大概讲解了一下整体模型或者从MSDN上COPY过来,未能对各模型之间的接口做详细讲解。了解.NETRemoting的朋友都知道,它的实现基本上都是通过接口与接口之间的串联形成处理管道,这也让我们想起来了设计模式中首要提及的面向对象设计思想“面向接口编程”。

本人在学习.NETRemoting的时候还是比较痛苦的,由于中文资料缺乏只能通过搜索网上零零散散的知识点再通过拼命的理解、换位思考、提高抽象层次,才终于参透为什么那么多接口之间的关系,在这里小弟会一一讲解那些诸如IClientChannelSink、IClientChannelSinkProvider、IMessageSink等等接口到底是什么意思,提供程序与信道接收器之间又是啥关系,在信道处理模型中是怎么利用多态来设计的。

.NETRemoting处理管道

在.NETRemoting中它的整体处理模型有点像ASP.NET中的HTTP处理管道模型,消息从最上面的入口进入然后一个一个的传递到各个处理模块中,这样就形成了一个抽象的处理管道。

图1(客户端信道处理):

这是客户端信道处理模型的大概结构。任何客户端对跨远程处理都是通过代理进行的,代理分为两部分一种是透明代理,一种是真实代理;

透明代理是通过真实代理动态生成的,透明代理的所有成员都是客户端将要调用的远程对象的一个虚拟镜像。为什么要说是虚拟的,是因为透明代理只是方便客户端调用的,当我们NEW一个远程对象时,系统将为我们动态的生成一个继承自你NEW的那个对象的代码,然后动态生成内存透明代理对象。

透明代理包括了对真实代理的调用,真正的幕后主脑是RealProxy对象,它包括了对信道处理结构的引用。在上面的图1中为什么真实代理是用IMessageSink接口来启动整个信道处理模型的。这里也是最为难理解的入口点,下面我们将自己的抽象能力提升一个层次才能知道系统为什么要这样设计。[王清培版权所有,转载请给出署名]
图2:(服务器端信道处理)

.NETRemoting饶人的名词解释

不管是服务器端还是客户端都是通过信道处理模型来对双方的消息进行一系列的处理。客户端的信道处理和服务器端的信道处理有点区别,这些区别是来自它们的更高层次的抽象。这句话可能根本不好理解,从我上面的两幅图中我想读者多多少少能理解点意思来,在信道处理管道中都是通过每个信道接收器提供程序来创建有处理能的信道接收器。

我们先来解决一些会给我们理解带来困扰的英文单词:

Channel:在系统中它表示信道,也就是处理管道。

IChannel:表示一个抽象的处理管理,用来标识一个管道的信息,如管道名称,管道级别之类的。

Sink:接收器,也就是管道中的消息处理环节,系统通常用这个单词来组合,也会让初学者容易参数混淆(看过这篇文章后你就不会在混淆了)。

Provider:一般表示为提供程序,在.NETRemoting里面的提供程序这是某些环境的一个入口点,比如在系统中序列化环节,系统通过SoapClientFormatterSinkProvider提供程序来创建SoapClientFormatterSink信道接收器,这里的提供程序是表示即将进入处理过程,这个处理过程表示某一个处理环节,在这个处理环节中可能包括很多用来接收要处理的信息,所有叫做信息接收器,也就是sink结尾的接口。

IClientChannelSinkProvider:客户端信道接收器提供程序,也就是图1中表示的每个对消息的处理环节部分。该接口有一个NEXT属性用来连接下一个提供程序,这样就串联起一个抽象的处理管道。我们要记住提供程序这是创建处理环节的入口点,真正用来处理消息的是sink结尾的接口,这里也就是IClientChannelSInk对象。

IClientChannelSink:客户端消息接收器,用于在管道处理中对消息的传递进行个性化修改,我们可以在这个里面进行一些消息传递的压缩、编码等等。

IServerChannelSinkProvider:服务器端信道接收器提供程序,和客户端是相同的功能,都是用来创建信息接收器用的,只不过这里是服务器端的信息处理。

IServerChannelSink:服务器端消息接收器,用来在服务器端进行消息的处理,可以对传输过来的消息进行解压缩、反编码等等,与客户端功能是对应的。

IMessageSink:入门的朋友可能会对这个接口参数误解,这是啥接口啊?我不是有了Sink类型的接口了吗?为什么还要消息接口?其实这里也很好理解,为什么需要各种各样的接口,这就是我上面所说的“将自己的抽象能力提升一个高度”才能理解。如果对接口编程不是很熟悉的朋友可能是不太好理解。每种接口表示某种抽象,我打个比方,如果有一个对象它既实现了IMan接口,又实现了IDog接口,它在与人交流的时候我们知道它是实现了人类接口的对象,所以我们认识它并且能与它进行交流,如果它与狗交流的话那么狗狗也是认识它的因为它也实现了狗的接口。从这里讲,实现多个接口的对象具有很深的抽象层次,在某些场合它被视为一种类型,在另外一些场合它又被视为另一种类型。下面我们讲到具体的接口实现时,就明白我所说的意思了。

.NETRemoting信道接收器

上面的名词解释的差不多了,我们来动动手吧;

按照上面的客户端信道模型图,我们先来实现一个IClientChannelSinkProvider接口,我们将利用这个接口来创建一个能处理消息的IClientChannelSink接口。

 

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Lifetime;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Ipc;

namespace MyClassLibrary.CustomProvider
{
    public class ClientCustomProvider : IClientChannelSinkProvider
    {
        #region IClientChannelSinkProvider 成员
        /// <summary>
        /// 创建该提供程序的信道接收器,所有的信道接收器提供程序均要实现CreateSink方法,保证所有的信道接收器能串联起来。
        /// </summary>
        /// <param name="channel">上下文发送接口</param>
        /// <param name="url">对象的终结点</param>
        /// <param name="remoteChannelData">信道数据</param>
        /// <returns>IClientChannelSink客户端信道接收器接口</returns>
        public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData)
        {
            return new ClientCustomSink(Next.CreateSink(channel, url, remoteChannelData));
        }
        /// <summary>
        /// 获取下一个信道接收器提供程序。通过这里的NEXT属性将所有的信道接收器连接起来。
        /// </summary>
        public IClientChannelSinkProvider Next { get; set; }

        #endregion
    }
}

 

这里是我们创建客户端接收器提供程序的代码,在这里我们创建个一个ClientCustomSink信道接收器对象,但是在构造函数中我们用NEXT属性又创建了一个信道接收器作为参数传入到对象,这样做的目的就是能保证每一个消息接收器处理完成后能接着传递给下一个信息接收器处理。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Lifetime;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Ipc;

namespace MyClassLibrary.CustomProvider
{
    public class ClientCustomSink : BaseChannelSinkWithProperties, IClientChannelSink
    {
        IClientChannelSink nextchannelsink;
        public ClientCustomSink(IClientChannelSink nextchannel)
        {
            nextchannelsink = nextchannel;
        }
        #region IClientChannelSink 成员

        public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, System.IO.Stream stream)
        {
            nextchannelsink.AsyncProcessRequest(sinkStack, msg, headers, stream);
        }

        public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, System.IO.Stream stream)
        {
            nextchannelsink.AsyncProcessResponse(sinkStack, state, headers, stream);
        }

        public System.IO.Stream GetRequestStream(IMessage msg, ITransportHeaders headers)
        {
            msg.Properties.Add("自定义消息", "自定义消息值");
            return nextchannelsink.GetRequestStream(msg, headers);
        }

        public IClientChannelSink NextChannelSink
        {
            get { return nextchannelsink; }
        }

        public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, System.IO.Stream requestStream, out ITransportHeaders responseHeaders, out System.IO.Stream responseStream)
        {
            nextchannelsink.ProcessMessage(msg, requestHeaders, requestStream, out responseHeaders, out responseStream);
        }
        public IDictionary NextProperise
        {
            get { return nextchannelsink.Properties; }
        }
        #endregion
    }
}

 

有一这里的代码太长我就不加注释了。这个对象主要实现了IClientChannelSink接口,也就是信道接收器接口,在这个接口面明前比Provider提供程序多了很多东西。这里才是真正处理的地方,系统为了代码的整洁性将提供程序与信息接收器分离开,其实也可以将这两个接口进行合并。在构造函数里面我们用一个私有的IClientChannelSink保存了下一个Sink。该对象还实现了BaseChannelSinkWithProperties抽象类,这个类是用来获取下一个信道接收器的内部属性用的。如果我们没有实现这个抽象类就要自己实现NextProperise属性。[王清培版权所有,转载请给出署名]

其实在IClientChannelSink里面比较重要的就是GetRequestStream和ProcessMessage两个方法,一个是用来获取即将要发送的Stream流,一个是进行发送的方法。为了便于大家的理解,请看图3:

我上面的处理流程不一定就是GetRequestStream是第一步,但是它是ProcessMessage方法的上一步,所以我用1、2表示。当GetRequestStream到了最后一个Sink时,系统将进行最后的调用,也就是进行远程发送了。
在GetRequestStream中我们加入了一些自己定义的数据,我们在通过服务器端的IServerChannelSink获取这消息。

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Lifetime;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Ipc;

namespace MyClassLibrary.CustomProvider
{
    public class ServerCustomSink : BaseChannelSinkWithProperties, IServerChannelSink
    {
        private IServerChannelSink iserverchannelsink;
        public ServerCustomSink(IServerChannelSink serverchannel)
        {
            iserverchannelsink = serverchannel;
        }
        #region IServerChannelSink 成员

        public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers, System.IO.Stream stream)
        {
            if (iserverchannelsink != null)
                iserverchannelsink.AsyncProcessResponse(sinkStack, state, msg, headers, stream);
            throw new NotImplementedException();
        }

        public System.IO.Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers)
        {
            if (iserverchannelsink != null)
                iserverchannelsink.GetResponseStream(sinkStack, state, msg, headers);
            throw new NotImplementedException();
        }

        public IServerChannelSink NextChannelSink
        {
            get { return iserverchannelsink; }
        }

        public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders,
            System.IO.Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out System.IO.Stream responseStream)
        {
            sinkStack.Push(this, "ok");
            return NextChannelSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, requestStream, out responseMsg, out responseHeaders, out responseStream);
        }

        #endregion

    }
}

 

图4:

 

总结:

.NETRemoting是一个非常成熟的CLR远程处理框架,其实现的原理也很复杂,基本上每一部分都可以扩展,它的内部实现都是通过面向接口来的,接口的粒度也非常小。从IXXXChannelSinkProvider和IXXXChannelSink两种接口就可以看出来,里面的空间很大。

本篇文章只是本人在学习.NETRemoting过程中的一点小小的感悟献给那些还没有搞清楚Remoting的基本框架的朋友。谢谢

时间: 2024-07-29 13:21:35

.NET简谈组件程序设计之(详解NetRemoting结构)的相关文章

.NET简谈组件程序设计之(渗入序列化过程)

在本人的上一篇文章".NET简谈组件程序设计之(初识序列化.持久化) "中,我们基本上了解了什么叫序列化和持久化.通过系统为我们提供的服务,我们可以很方便的进行二进制序列化.SOAP协议序列化. 今天这篇文章是来讲解怎么运用一些高级的功能,在序列化.反序列化过程中进行一些控制.[王清培版权所有,转载请给出署名] 这里穿插一句题外话:其实在我们自己编写组件的时候真的有好多东西可以借鉴.NET平台的一些优点,它的功能都不是死的,可以订阅.可以切入,在我们编写组件的时候,我们其实也要好好考虑

.NET简谈组件程序设计之(初识NetRemoting)

在本人的".NET简谈组件程序设计之(初识远程调用)  "一文中,我们了解到什么是远程调用或者说在.NET平台上远程调用是什么样子的,可能和偏低层(Socket\Rpc)的远程调用有点距离.这只是系统为我们封装了假象而已,看不见不代表没有这逻辑,是为我们减轻了劳动负担.[王清培版权所有,转载请给出署名] 这篇文章我们来简单的了解一下在.NET平台上有一个强有力的远程调用武器,也是上一篇文章中我一笔带过的远程英雄.NetRemoting. 其实在.NET平台里面到处都能看见Remotin

.NET简谈组件程序设计之(上下文与同步域)

我们继续学习.NET多线程技术,这篇文章的内容可能有点复杂.在打破常理之后,换一种新的思考模型最为头疼.这篇文章里面会涉及到一些不太常见的概念,比如:上下文.同步域等等.我也是最近才接触这些关于组件编程方面的高深技术,大家一起学习,再大的困难也是有时间限制的,只要我们坚持. 在本人的上一篇文章".NET简谈组件程序设计之(多线程与并发管理一)"中,只是初步的带领我们学习一下关于多线程的一些基本的原理,包括线程切换,线程的开始.执行.等待.结束. 这篇文章的重点是学习关于线程的同步.互斥

.NET简谈组件程序设计之(初识.NET线程Thread)

由于多线程的内容比较多我会用几篇文章来讲解. 多线程在我们日常开发过程中用的很多,上一篇".NET简谈组件程序设计之(异步委托) "详细的讲解了基于委托的多线程使用,委托是基于后台线程池的原理,这篇文章将主要介绍直接使用Thread对象来实现多线程. 当然使用Thread没有使用Delegate那么容易,毕竟多线程跟异步调用是两个相差很大的技术方向,我也是略懂点皮毛,在此献丑给大家,如有讲的不对的地方还请指出.[王清培版权所有,转载请给出署名] 我们先来理解几个概念,以方便我们学习.

.NET简谈组件程序设计之(初识远程调用)

在.NET1.0版本出来的时候,要想进行远程调用基本上都是通过WebService的方式.而随着.NET2.0版本的出现,我们可以通过一个更加方便且高扩展性的框架来进行编写远程调用的程序,也就是我们都比较熟悉的.NetRemoting. 网上对.NetRemoting技术讲解的文章不计其数,但是很少有一本比较全面的.系统的学习书籍.我们都是从哪些零散的知识里慢慢摸索,效果不太理想. 今天我也来简单的介绍一下我理解的Remoting.不仔细研究一下还真不知道它的厉害,完全的托管平台.高扩展性.灵活

.NET简谈组件程序设计之(手动同步)

在上一篇文章".NET简谈组件程序设计之(上下文与同步域)"中,我们学习了关于一些上下文和同步域的概念,可以利用这两个技术来进行自动同步. 今天我们主要学习怎么手动来执行同步,能从更小的粒度进行封锁,以达到最大程度的吞吐量.[王清培版权所有,转载请给出署名] 我们知道线程是进程的运行实体,进程是资源分配单位,而线程是执行单位.照书上所说,线程是程序的执行路径,当我们分配一个线程的时候,要确定线程的执行路径是什么,也就是代码中的ThreadStart委托所指向的入口点方法. 一旦我们手动

.NET简谈组件程序设计之(delegate与event关系)

 本人最近一段时间在学习关于.NET组件编程方面的技术,在学习过程中确实有很多好的东西需要与大家分享.[王清培版权所有,转载请给出署名] 关于什么叫组件编程,其实就是利用.NET来开发基于组件模型的程序,面向组件编程而非面向对象编程,这是一个高度,没有很长时间的学习与磨练 是体会不到这个感觉的.我们现在的开发思想应该是以面向对象为主的,但是如何提升这个高度,只有慢慢的学习了. 其实面向组件编程包含了面向对象编程思想,将一组功能独立的封装起来供以后重复使用,但是在开发组件的过程中需要使用到面向对象

.NET简谈组件程序设计之(初识序列化、持久化)

 今天我们来学习在组件开发中经常用到的也是比较重要的技术"序列化". 序列化这个名词对初学者来说不太容易理解,有点抽象.我们还是用传统的分词解释吧,肯定能搞懂它的用意是什么. 解释:数学上,序列是被排成一列的对象(或事件):这样,每个元素不是在其他元素之前,就是在其他元素之后.这里,元素之间的顺序非常重要. 那么我们对照这样的解释来分析一下我们程序中的序列化什么意思.都知道对象的状态是在内存中实时存着的,对象的状态在初始化的时候是通过系统分配的,在后期的程序运行过程中可能对它进行过一些

.NET简谈组件程序设计之(AppDomain应用程序域)

最近在苦学.NET底层框架模型,发现.NET深入真的不是一般的难,不开源.没有相关系统的官方的书籍做学习资料,只能零散的看MSDN.要想摸熟.NET的模型真的并非易事.慢慢来吧.[王清培版权所有,转载请给出署名] .NET应用程序域(AppDomain)是我们所有.NET应用程序的逻辑宿主容器.初次接触会感觉到AppDomain离我们日常开发比较远,不常用到.其实是我们很少接触一些复杂而底层的系统结构.在日常的开发中,我们多数是基于数据库的管理信息系统(MIS),做增.删.改.查的操作.我始终认