(一) Remoting框架图

这是msdn上关于Remoting客户端与服务器端进行通信的示意图。客户端与服务端的通信是通过发送消息来实现的。消息的处理是由客户端,服务端创建的一系列的通信信道来处理的。客户端通过代理将消息的调用转换为IMessage,IMessage传入通信处理链,经过客户端FormatterSink转换为消息流,然后通过IClientChannelSink对消息进一步处理,然后进入TransportSink传输到服务端。服务端收到消息后,通过FormatterSink将流转换为消息,让远程对象进行处理。这种消息的处理流程统称为信道接收器(如格式化程序、传输或堆栈生成器接收器),但您可以自定义信道接收器链,以使用消息或流执行特殊任务。 每个信道接收器要么实现 IClientChannelSink,要么实现 IServerChannelSink 客户端上的第一个信道接收器还必须实现 IMessageSink 它通常实现 IClientFormatterSink(继承自 IMessageSink、IChannelSinkBase 和 IClientChannelSink),被称为格式化程序接收器,因为它将传入的消息转换为流(IMessage对象)。

信道接收器提供程序(实现 IClientChannelSinkProvider、IClientFormatterSinkProvider 或 IServerChannelSinkProvider 接口的对象)负责创建处理 .NET 远程处理消息的信道接收器。 在激活远程类型时,将从信道中检索信道接收器提供程序,并在接收器提供程序上调用 CreateSink 方法,以检索链中的第一个信道接收器。

信道接收器负责在客户端和服务器之间传输消息。 它们彼此链接而形成一条链。 当在接收器提供程序上调用 CreateSink方法时,该方法应执行下列操作:

· 创建一个信道接收器。

· 在链中的下一个接收器提供程序上调用 CreateSink

· 确保下一个接收器和当前接收器链接在一起。

· 将其接收器返回调用方。

信道接收器负责将这些接收器上执行的所有调用转发给链中的下一个接收器,同时还应当提供一种机制,用来存储对下一个接收器的引用。

对于沿接收链发送哪些内容,信道接收器有很大的灵活性。 例如,在发送实际的序列化原始消息之前协商身份验证的安全接收器可以截获完整的信道消息,用其自己的内容替换内容流,然后再沿着接收器链将内容流一直发送到远程应用程序域。 在返回过程中,安全接收器可以截获答复消息,并与远程应用程序域中对应的安全接收器进行对话。 协议一经达成,起始安全接收器便可以将原始内容流一直发送到远程应用程序域。

信道接收器链中的消息处理

一旦 .NET 远程处理系统找到可以处理消息的信道,该信道便将消息传递给格式化程序信道接收器,此过程通过调用 SyncProcessMessage(或 AsyncProcessMessage)来实现。 格式化程序接收器创建传输标头数组,并对下一个接收器调用 GetRequestStream。 此调用沿接收器链向下转发,并且任何接收器均可创建将被传递回格式化程序接收器的请求流。 如果 GetRequestStream 返回 null 引用(在 Visual Basic 为 Nothing),格式化程序接收器将创建自己的接收器以用于序列化。 此调用一经返回,即会序列化消息,并在接收器链的第一个信道接收器上调用相应的消息处理方法。

虽然接收器无法将数据写入流,但可以从流中读取数据,或沿所需位置传递新流。 接收器还可以向标头数组添加标头(如果它们以前没有在下一个接收器上调用 GetRequestStream),并在将调用转发到下一个接收器之前将它们本身添加到接收器堆栈。 (同步堆栈的作用是允许异步调用在其完成时对调用方进行回调。) 当调用到达链末端的传输接收器时,传输接收器会将标头和序列化消息通过信道发送到服务器,在该服务器上,整个过程将反向进行。 传输接收器(位于服务器上)从服务器端的流中检索标头和序列化消息,并通过接收器链转发它们,直至送达格式化程序接收器为止。 格式化程序接收器反序列化该消息并将其转发给 .NET 远程处理系统,消息在此处被恢复为方法调用,并在服务器对象上被调用。

创建信道接收器链

若要创建新的信道链,必须实现和配置 .NET 远程处理系统,以识别 IServerChannelSinkProvider 或IClientChannelSinkProvider 实现,您可以借助这两个实现来创建自定义的 IClientChannelSink 或IServerChannelSink 实现,或检索链中的下一个接收器。 您可以使用 BaseChannelSinkWithProperties 抽象类来帮助实现自定义的信道接收器。

生成信道接收器提供程序

在构造信道时,应用程序可以将服务器或客户端信道接收器提供程序作为参数来提供。 信道接收器提供程序应当存储在一个链中,在向信道构造函数传递外部信道接收器提供程序之前,开发人员负责将所有的信道接收器提供程序链接在一起。 为此,信道接收器提供程序实现了 Next 属性。 下面的代码示例阐释如何生成客户端信道接收器提供程序。 完整的示例可以在远程处理示例:信道接收器提供程序中找到。

格式化程序接收器

格式化程序接收器会将信道消息作为实现 IMessage 的对象而序列化为消息流。 有些格式化程序接收器实现使用系统提供的格式化程序类型(BinaryFormatter和 SoapFormatter)。 其他实现可以使用自己的方法将信道消息转换为流。

格式化程序接收器的作用是生成所需的标头并将消息序列化为流。 在格式化程序接收器之后,消息将通过SyncProcessMessage 或 AsyncProcessMessage 调用转发给接收器链中的所有接收器。 在此阶段,消息已经序列化,不能再进行修改。

 

必须创建或修改消息的接收器本身一定要放在接收器链中的格式化程序之前。 通过实现 IClientFormatterSink 可以轻松地实现这一点,从而告诉系统它已经具有对该格式化程序接收器的引用。 然后可以将真实的格式化程序接收器放到该接收器链中后面的位置上。

在返回过程中,格式化程序接收器将消息流转换回信道消息对象(返回消息)。 客户端上的第一个接收器必须实现IClientFormatterSink 接口。 当 CreateSink 返回信道时,返回的引用将被强制转换为 IClientFormatterSink 类型,以便可以调用 SyncProcessMessage 方法。 请记住,IClientFormatterSink 是从 IMessageSink 派生而来的。 如果强制转换失败,系统会引发异常。

自定义信道接收器

在客户端上,自定义信道接收器将被插入到格式化程序接收器与最后一个传输接收器之间的对象链中。 通过在客户端或服务器信道中插入自定义信道接收器,您可以在下面所列的时间点之一处理 IMessage

· 在表示为消息的调用被转换为流,并通过网络发送的过程中。

· 在流被从网络中取出,并发送至服务器或代理对象(在客户端)上远程对象前面最后一个消息接收器的过程中。

自定义接收器可以从流中读取数据或向流写入数据(具体取决于是传出调用还是传入调用),还可以根据需要将附加信息添加到标头。 在此阶段,消息已被格式化程序序列化,不能再进行修改。 当消息调用被转发到链末端的传输接收器时,传输接收器将标头写入流,并使用信道专用的传输协议将该流转发给服务器上的传输接收器。

传输接收器

传输接收器是客户端上链中的最后一个接收器,同时也是服务器上链中的第一个接收器。 除了传输序列化的消息外,传输接收器还负责将标头发送到服务器,并在服务器返回调用时检索标头和流。 这些接收器都内置于信道中,无法扩展。

(二) TransparentProxy与RealProxy

上面说到代理,不得不提到TransparentProxy(透明代理)与RealProxy(真实代理),代理是本地对象与远程对象的桥梁,通过继承RealProxy,将方法的调用转换为基于Invoke(IMessage msg)的形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Create a custom 'RealProxy'.
public class MyProxy : RealProxy
{
   String myURIString;
   MarshalByRefObject myMarshalByRefObject;  
 
   [PermissionSet(SecurityAction.LinkDemand)]
   public MyProxy(Type myType) : base(myType)
   {
      // RealProxy uses the Type to generate a transparent proxy.
      myMarshalByRefObject = (MarshalByRefObject)Activator.CreateInstance((myType));
      // Get 'ObjRef', for transmission serialization between application domains.
      ObjRef myObjRef = RemotingServices.Marshal(myMarshalByRefObject);
      // Get the 'URI' property of 'ObjRef' and store it.
      myURIString = myObjRef.URI;
      Console.WriteLine("URI :{0}", myObjRef.URI);
   }
 
   [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
   public override IMessage Invoke(IMessage myIMessage)
   {
      Console.WriteLine("MyProxy.Invoke Start");
      Console.WriteLine("");
 
      if (myIMessage is IMethodCallMessage)
         Console.WriteLine("IMethodCallMessage");
 
      if (myIMessage is IMethodReturnMessage)
         Console.WriteLine("IMethodReturnMessage");
 
      Type msgType = myIMessage.GetType();
      Console.WriteLine("Message Type: {0}", msgType.ToString());
      Console.WriteLine("Message Properties");
      IDictionary myIDictionary = myIMessage.Properties;
      // Set the '__Uri' property of 'IMessage' to 'URI' property of 'ObjRef'.
      myIDictionary["__Uri"] = myURIString;
      IDictionaryEnumerator myIDictionaryEnumerator =
         (IDictionaryEnumerator) myIDictionary.GetEnumerator();
 
      while (myIDictionaryEnumerator.MoveNext())
      {
         Object myKey = myIDictionaryEnumerator.Key;
         String myKeyName = myKey.ToString();
         Object myValue = myIDictionaryEnumerator.Value;
 
         Console.WriteLine("\t{0} : {1}", myKeyName,
            myIDictionaryEnumerator.Value);
         if (myKeyName == "__Args")
         {
            Object[] myObjectArray = (Object[])myValue;
            for (int aIndex = 0; aIndex < myObjectArray.Length; aIndex++)
               Console.WriteLine("\t\targ: {0} myValue: {1}", aIndex,
                  myObjectArray[aIndex]);
         }
 
         if ((myKeyName == "__MethodSignature") && (null != myValue))
         {
            Object[] myObjectArray = (Object[])myValue;
            for (int aIndex = 0; aIndex < myObjectArray.Length; aIndex++)
               Console.WriteLine("\t\targ: {0} myValue: {1}", aIndex,
                  myObjectArray[aIndex]);
         }
      }
 
      IMessage myReturnMessage;
 
      myIDictionary["__Uri"] = myURIString;
      Console.WriteLine("__Uri {0}", myIDictionary["__Uri"]);
 
      Console.WriteLine("ChannelServices.SyncDispatchMessage");
      myReturnMessage = ChannelServices.SyncDispatchMessage(myIMessage);
 
      // Push return value and OUT parameters back onto stack.
 
      IMethodReturnMessage myMethodReturnMessage = (IMethodReturnMessage)
         myReturnMessage;
      Console.WriteLine("IMethodReturnMessage.ReturnValue: {0}",
         myMethodReturnMessage.ReturnValue);
 
      Console.WriteLine("MyProxy.Invoke - Finish");
 
      return myReturnMessage;
   }
}

(三) IMessage

(四) IClientFormatterSinkProvider与IClientFormatterSink,IMessageSink

BinaryClientFormatterSinkProvider和SoapClientFormatterSinkProvider均实现了IClientChannelSinkProvider接口,该接口用来生成客户端信道中的IClientChannelSink

的实例对象BinaryClientFormatterSink和SoapClientFormatterSink来处理消息,及处理请求的数据流,我们来看看IClientChannelSinkProvider及IClientChannelSink的方法

public interface IClientChannelSinkProvider

{

IClientChannelSinkProvider Next { get; set; }

IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData);

}

IClientChannelSink的方法

public interface IClientChannelSink : IChannelSinkBase

{

IClientChannelSink NextChannelSink { get; }

void AsyncProcessRequest(IClientChannelSinkStack sinkStack, IMessage msg, ITransportHeaders headers, Stream stream);

void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, object state, ITransportHeaders headers, Stream stream);

Stream GetRequestStream(IMessage msg, ITransportHeaders headers);

void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream);

}

因此如果我们需要自定义自己的消息处理,客户端可以实现IClientChannelSinkProvider及IClientChannelSink接口,然后通过注册或配置文件注册到消息处理链中。客户端上的第一个接收器必须实现 IClientFormatterSink 接口,因为在这里消息将被序列化为消息流。

(五) IServerFormatterSinkProvider与IServerChannelSink

而在服务端通过实现ISeverChannelSinkProvider的BinaryServerFormatterSinkProvider和SoapServerFormatterSinkProvider,生成消息处理的BinarySeverFormatterSink和SoapServerFormatterSink来对客户端发送过来的消息进行处理,这两个类实现了ISeverChannelSink,这个接口是服务器处理消息必须实现的接口,而ISeverChannelSinkProvider服务端消息处理对象提供者必须实现的接口

IServerChannelSinkProvider的方法

public interface IServerChannelSinkProvider

{

IServerChannelSinkProvider Next { get; set; }

IServerChannelSink CreateSink(IChannelReceiver channel);

void GetChannelData(IChannelDataStore channelData);

}

IServerChannelSink的方法

public interface IServerChannelSink : IChannelSinkBase

{

IServerChannelSink NextChannelSink { get; }

void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers, Stream stream);

Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers);

ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream);

}

在服务端如果对消息处理进行扩展,这两个接口是必须实现的接口。

(六) IChannel

消息的传递可以通过HttpChannel,TcpChannel,IpcChannel来传递,关于信道的选择可以参考ms-help://MS.MSDNQTR.v90.chs/ws_fxremoting/html/c4959f26-a935-42fd-8dcf-0c35de5fb860.htm

(七) 扩展

若要创建新的信道链,必须实现和配置 .NET 远程处理系统,以识别 IServerChannelSinkProvider 或IClientChannelSinkProvider 实现,您可以借助这两个实现来创建自定义的 IClientChannelSink 或IServerChannelSink 实现,或检索链中的下一个接收器。 您可以使用 BaseChannelSinkWithProperties 抽象类来帮助实现自定义的信道接收器。

(八) Aop

IServerChannelSinkProvider的更多相关文章

  1. 第11章 .NET Remoting

    11.1理解remoting 11.1.1应用程序域基本概念 .NET提供了一项技术,使得跨应用程序域中的对象也可以相互访问,该技术就是.NET remoting.(185) 11.1.2应用程序域的 ...

  2. .NET高级代码审计(第五课) .NET Remoting反序列化漏洞

    0x00 前言 最近几天国外安全研究员Soroush Dalili (@irsdl)公布了.NET Remoting应用程序可能存在反序列化安全风险,当服务端使用HTTP信道中的SoapServerF ...

  3. COM+时代的自动事务

    最近看公司的遗留项目代码,调试的时候发现经常报分布式事务错误,可是整个代码里没有看见开启过事务,于是开始研究,发现了这个.Net Framework1.1时代的产物. namespace Busine ...

随机推荐

  1. Session 共享(Custom模式)By Memcached(原创)

    1.web.config配置: <machineKey decryptionKey="FD69B2EB9A11E3063518F1932E314E4AA1577BF0B824F369& ...

  2. java面试第十五天

    网络编程: 多线程+网络: 1.服务器端的等待客户连接代码( while(true) ),服务器端与单个客户端交互的代码放入线程体( run ) 2.客户端如有其他要求,与服务器交互的代码也要放入线程 ...

  3. 【Oracle】RAC 10.2.0.1升级10.2.0.5

    环境: OS:OEL5.6 RAC:10.2.0.1.0 相关环境变量: CRS_HOME /u01/app/oracle/product/10.2.0/db_1 ORACLE_HOME   /u01 ...

  4. 解决Cydia出现红字提示“Sub-process/usr/bin/dpkg returned an error code(2)

    进入此路径/var/lib/dpkg/,修改红框中的文件名,依次修改为:"available" 重新命名为 "available-bak":"stat ...

  5. eclipse中java项目转成Web项目

    1.找到项目目录下的.project文件 2.编辑.project文件,找到<natures>...</natures> 3.2中找到的结点中加下面的的代码 <natur ...

  6. 在命令行上 Ubuntu 下使用 mutt 和 msmtp 发送 Gmail 邮件

    在命令行写email from ubuntu 参考:      http://www.habadog.com/2011/11/23/send-mail-with-msmtp-mutt-linux    ...

  7. oracle 快速备份表数据

      oracle 快速备份表数据 CreateTime--2018年2月28日17:04:50 Author:Marydon UpdateTime--2017年1月20日11:45:07 1.1.9. ...

  8. Android学习之Menu

    1.普通的Menu 在Activity中覆盖onCreateOptionsMenu(Menu menu)方法,该方法负责生产menu,它是一个回调函数,即当按下手机设备上的menubutton时And ...

  9. 几道比较难的SQL题

    上条记录和下一条记录 在展示博客文章时,在文章底部需要展示上一篇文章和下一篇文章,文章的排序当然是按照时间排序的. 选定下一条时可以用limit 1来实现,选取上一条时可以倒序limit 1实现 (S ...

  10. Mac下搭建svn服务器和XCode配置svn

    先打开命令行终端. 1.创建svn repository svnadmin create /yourpath/svnroot/repository 2.配置svn用户权限. / yourpath /s ...