(一) 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( " {0} : {1}" , myKeyName, myIDictionaryEnumerator.Value); if (myKeyName == "__Args" ) { Object[] myObjectArray = (Object[])myValue; for ( int aIndex = 0; aIndex < myObjectArray.Length; aIndex++) Console.WriteLine( " arg: {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( " arg: {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