Advanced .NET Remoting: 第 8 章 创建连接器
第 8 章 创建连接器
上一章向您展示了各种类型的连接器,以及它们对于请求的同步和异步处理过程。到目前为止,我一直忽略的一个最为重要的步骤是:初始化连接器和连接器链。连接器通常既不是直接待代码中创建,也不是在配置文件中定义出来。相反,是通过链接器链来设置的。它最终在后台返回连接器链。本章将向您展示构建自己的连接器链的基础。这些定制的连接器将在第 9 章中实现。
8.1 理解连接器提供器

如你在第 4 章中所见,你可以使用如下所示的方式,通过 .NET 配置文件来定义连接器链。( 该示例用于客户端的配置文件,对于服务器端连接器链,你需要将 clientProviders 替换为 serverProviders )
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<provider type="MySinks.SomeMessageSinkProvider, Client" />
<formatter ref="soap" />
<provider type="MySinks.SomeClientChannelSinkProvider, Client" />
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
<configuration>
为了更为透彻地说明该示例,我将其中的 \<formatter ref="soap" /> 替换为使用 machine.config 中真实的值 ( 包括其中必须的强签名部分 )
这样,完整的链现在看起来是这样的:
<provider type="MySinks.SomeMessageSinkProvider, Client" />
<formatter type="System.Runtime.Remoting.Channels.SoapClientFormatterSinkProvider,
System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
<provider type="MySinks.SomeClientChannelSinkProvider, Client" />
如你所见,在这些示例中,该连接器链 不是 通过 sinks 的名/值对来定义的 ( 也许应该是这样,例如 SoapClientFormatterSink )。相反,是使用连接器 提供器 来完成的,提供器既可以在客户端,也可以在服务器端使用,它至少需要实现如下一个接口:
public interface IClientChannelSinkProvider {
IClientChannelSinkProvider Next { get; set; }
IClientChannelSink CreateSink(IChannelSender channel,
string url,
object remoteChannelData);
}
public interface IServerChannelSinkProvider {
IServerChannelSinkProvider Next { get; set; }
IServerChannelSink CreateSink(IChannelReceiver channel);
void GetChannelData(IChannelDataStore channelData);
}
你可以在接口定义中看到,每个提供器中使用了 Next 属性来构建真实的调用链。
8.1.1 创建客户端的连接器
在加载了前面的示例配置文件之后,提供器链将被如图 8-1 所示的对象调用形式构建出来。前面 3 个提供器将通过配置文件加载,而最后一个 (HttpClientTransportSinkProvider) 则被 HTTP 通道默认初始化出来。

图 8-1 提供器链
在客户端,这些连接器提供器被关联到客户端的通道。你可以使用如下代码来访问这个通道对象:
IChannel chnl = ChannelServices.GetChannel("http");
注意到代码中的 "http" 表示通道的唯一名称。对于 HTTP 与二进制通道来说,名称是在 machine.config 中设置的。
该通道对象创建的相关连接器如图 8-2 所示:

图 8-2 使用提供的连接器提供器创建的 IChannel
当一个远程的服务器端激活对象 (SAO ) 被创建的时候 ( 对于 SAO,额外的 ConstructionCall 消息被发送到服务器,并使用代理的标识对象填充 ),在幕后会发生一系列事情。在使用 new 操作符的时候,或者调用 Activator.GetObject() 的时候,方法 RemotingServices.Connect() 被调用,在调用之后发生的事情如图 8-3 所示:

图 8-3 通过连接器提供器链创建连接器
在通过 RemotingServices 调用之后,ChannelServices 在每个注册的通道上调用 CreateMessageSink() ,直到其中之一接受作为参数之一传递的 URL 地址。比如说是 HTTP 通道,它工作在任何以 http: 或者以 https: 开头的 URL 地址上,而 TCP 通道只接受使用 tcp: 协议。
当通道识别了指定的 URL 地址,它就会在客户端通道上调用 CreateMessageSink() 方法。
对于 HttpClientChannel 来说,随后在第一个通道提供器上调用 CreatSink() 方法 ( 如图 8-3 所示 )。不同的接收器提供器现在执行的操作,对应下面的代码:
public class SomeClientChannelSinkProvider: IClientChannelSinkProvider {
private IClientChannelSinkProvider next = null;
public IClientChannelSink CreateSink(IChannelSender channel,
string url, object remoteChannelData)
{
IClientChannelSink nextSink = null;
// checking for additional sink providers
if (next != null)
{
nextSink = next.CreateSink(channel,url,remoteChannelData);
}
// returning first entry of a sink chain
return new SomeClientChannelSink(nextSink);
}
}
每个接收器提供器首先在提供器链上的下一个条目上调用 CreateSink() 方法,通过返回的结果,然后在其上返回自己的提供器。在调用链的最开始放置一个新的接收器的显式语法并没有指出。但是在此示例中,SomeClientSink 提供器的构造函数接受一个 IClientChannelSink 对象作为参数,并设置到它的 _nextChnlSink 实例变量中,如下面代码片段所示 ( 重复一下,这里只有部分代码 )
public class SomeClientChannelSink: IClientChannelSink {
private IClientChannelSink _nextChnlSink;
public SomeClientChannelSink (IClientChannelSink next)
{
_nextChnlSink = next as IClientChannelSink;
}
}
通过调用 ChannelServices.CreateMessageSink() 方法返回的完整的连接器链,随后被连接到一对新的 TransparentProxy/RealProxy 成对标识对象上,如图 8-4 所示

图 8-4 连接到 TransparentProxy 的第一个 IMessageSink
8.1.2 创建服务器端连接器
创建服务器端的连接器与创建客户端连接器有一点不同。如你前面所见,在客户端,当引用一个要求的远程对象的时候,需要的连接器被创建。与此相反,服务器端的对象是在通道被注册的时候立即创建。
当服务器端的通道通过配置文件中的定义创建的时候,使用如下的构造函数:
public HttpServerChannel(IDictionary properties, IServerChannelSinkProvider sinkProvider)
第一个参数 IDictionary 来自 <channel> 配置节的定义。例如,当你的配置文件如下所示,包含下述内容:
<channel ref="http" port="1234" />
那么,properties 字典中将会包含一个键为 port 值为 1234 的条目。
而在构造函数的 sinkProvider 参数,来自连接器链的第一个将会被传递给通道。该链使用配置文件中的 <serverProvider> 配置节构造。
在通道设置过程中,它从 HTTP 通道的构造函数开始,现在会发生两件事中的之一。如果没有 <serverProvider> 配置,默认的连接器链将被创建,如图 8-5 所示。

图 8-5 HttpServerChannel 的默认连接器链
当在配置文件中指定了 <serverProviders> 的时候,连接器链将通过这些定义创建,不会使用默认的连接器提供器。
注意:有意思的是,在本场景下,你将不能通过在你的 SAO 对象上在 URL 上使用
?WSDL参数来生成 WSDL 定义,除非在 ` 配置节中显式指定了 SdlChannelSinkProvider
在提供器链被创建之后,ChannelServices.CreateServerChannelSinkChain() 方法被调用。该方法将连接器提供器链作为参数。它会随后遍历该链条,并在调用 CreateSink() 方法之前,添加 DispatchChannelSinkProvider 对象到该链的最后。最后,它返回生成的连接器链条。当通过 ChannelServices 收到这个对象之后,HttpServerChannel 对象将添加一个 HttpServerTransportSink 作为第一个元素。最后的服务器端通道对象如图 8-6 所示。

图 8-6 完整的服务器端 HTTP 通道连接器栈
使用动态连接器
如你在上一章所见,客户端和服务器端的连接器链都可以调用动态连接器。在服务器端是通过 CrossContextChannel 而客户端是通过 ClientContextTerminatorSink 来完成的。
动态连接器关联到特定的 上下文 ( 你可以在第 11 章了解更多关于上下文内容 ),进而被传递了上下文边界的所有调用所调用。它们不能被赋予特定的通道,甚至可以被本地跨上下文,或者跨应用程序域所调用。连接器通过动态上下文属性创建,这是实现了 IDynamicProperty 和 IContributeDynamicSink 接口的类。
注意:IContributeDynamicSink 可以看作是动态连接器的连接器提供器。
相关的接口如下所示:
public interface IDynamicProperty {
string Name { get; }
}
public interface IContributeDynamicSink {
IDynamicMessageSink GetDynamicSink();
}
动态连接器本身,通过 GetDynamicSink() 方法返回,实现如下的 IDynamicMessageSink 接口:
public interface IDynamicMessageSink {
void ProcessMessageStart(IMessage reqMsg, bool bCliSide, bool bAsync);
void ProcessMessageFinish(IMessage replyMsg, bool bCliSide, bool bAsync);
}
如名称所暗示,在消息穿过接收器链之前,ProcessMessageStart() 方法将被调用,在消息被连接器处理之后,ProcessMessageFinish() 方法被调用。
下面的简化的动态连接器将会在控制台写出一行,只要消息穿越 remoting 边界。
public class MyDynamicSinkProvider: IDynamicProperty, IContributeDynamicSink
{
public string Name
{
get { return “MyDynamicSinkProvider"; }
}
public IDynamicMessageSink GetDynamicSink()
{
return new MyDynamicSink();
}
}
public class MyDynamicSink: IDynamicMessageSink
{
public void ProcessMessageStart(IMessage reqMsg, bool bCliSide, bool bAsync)
{
Console.WriteLine("--> MyDynamicSink: ProcessMessageStart");
}
public void ProcessMessageFinish(IMessage replyMsg, bool bCliSide, bool bAsync)
{
Console.WriteLine("--> MyDynamicSink: ProcessMessageFinish");
}
}
为了使用正确的上下文注册 IDynamicProperty,你可以使用如下代码:
Context ctx = Context.DefaultContext;
IDynamicProperty prp = new MyDynamicSinkProvider();
Context.RegisterDynamicProperty(prp, null, ctx);
小结
在本章中,你了解了创建各种类型的连接器。与上一章一起,你已经完全具备了实现你自己的连接器来扩展 .NET Remoting 功能。你还与动态上下文连接器有了第一次碰面,更详细内容将会在第 11 章中介绍。
我承认随后的两章内容有点多,但是在下一章,我将会为你的耐心奖励一些使用这里介绍的技术所创建的真实世界的连接器。
原文链接
Advanced .NET Remoting: 第 8 章 创建连接器的更多相关文章
- [Effective Java]第二章 创建和销毁对象
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 第一章 创建WEB项目
第一章 创建WEB项目 一.Eclipse创建WEB项目 方法/步骤1 首先,你要先打开Eclipse软件,打开后在工具栏依次点击[File]>>>[New]>>&g ...
- 《Effective Java 2nd》第2章 创建和销毁对象
目录 第1条:考虑使用静态工厂方法代替构造器 第2条:遇到多个构造器参数时考虑用构建器 第3条:用私有构造器或者枚举类型强化Singleton属性 第4条:通过私有构造器强化不可实例化的能力 第5条: ...
- Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏
Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏 即使是如今,非常多初学游戏开发的同学.在谈到Unity的时候.依旧会觉得Unity仅仅能用于制作3D游戏的. 实际上.Unity在2013 ...
- Rxjava2实战--第三章 创建操作符
Rxjava2实战--第三章 创建操作符 Rxjava的创建操作符 操作符 用途 just() 将一个或多个对象转换成发射这个或者这些对象的一个Observable from() 将一个Iterabl ...
- [Effective Java 读书笔记] 第二章 创建和销毁对象 第一条
第二章 创建和销毁对象 第一条 使用静态工厂方法替代构造器,原因: 静态工厂方法可以有不同的名字,也就是说,构造器只能通过参数的不同来区分不同的目的,静态工厂在名字上就能表达不同的目的 静态工厂方法 ...
- [ABP教程]第三章 创建、更新和删除图书
Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...
- [ABP教程]第一章 创建服务端
Web应用程序开发教程 - 第一章: 创建服务端 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发 ...
- suse 12 二进制部署 Kubernetets 1.19.7 - 第01章 - 创建CA证书和kubectl集群管理命令
文章目录 1.kubernetes集群部署 1.0.创建CA证书和秘钥 1.0.0.安装cfssl工具 1.0.1.创建根证书 1.0.2.创建证书签名请求文件 1.0.3.生成CA证书和秘钥 1.0 ...
- 《Linux命令行与shell脚本编程大全》第十七章 创建函数
可以将shell脚本代码放进函数中封装起来,这样就能在脚本中的任何地方多次使用它了. 17.1 基本的脚本函数 函数:是一个脚本代码块,可以为其命名并在代码中任何位置重用. 17.1.1 创建函数 有 ...
随机推荐
- Java以封装对象的方式读取CSV文件存储数据库
依赖 <!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv --> <dependency& ...
- linux cpufreq framework(5)_ARM big Little driver
1. 前言 也许大家会觉得奇怪:为什么Linux kernel把对ARM big·Lttile的支持放到了cpufreq的框架中? 众所周知,ARM的big·Little架构,也称作HMP(具体可参考 ...
- 墨天轮最受DBA欢迎的数据库技术文档-故障处理案例篇
在之前发布的<墨天轮最受欢迎的技术文档-容灾备份篇>中,大家说想看故障处理案例篇的内容,这不!编辑部快马加鞭给大家整理来了,希望能够帮助到大家. 数据库故障可能出现在内存.网络.CPU.硬 ...
- 把数字转换RMB形式
方法1 : var str = '12345679' let strNew = str.replace(/\B(?=(?:\d{3})+\b)/g, ',') // 匹配单词边界替换为逗号 方法2: ...
- kotlin协程——>select 表达式(实验性的)
select 表达式(实验性的) select 表达式可以同时等待多个挂起函数,并 选择 第⼀个可⽤的. 在通道中 select 我们现在有两个字符串⽣产者:fizz 和 buzz .其中 fizz ...
- c++11大括号初始化
C++11可以将{}初始化器用于任何类型(可以用等号,也可以不用) 数组.集合初始化 在C++11中,集合(列表)的初始化已经成为C++的一个基本功能,被称为"初始化列表": // ...
- Issac_GYM重要过程记录
1 下载相关文件 进入github中下载相关的文件 https://github.com/leggedrobotics/legged_gym 2 加载自己绘制的URTL文件 这个链接用来下载宇树的Go ...
- Httprunner生成Allure格式HTML报告
一.httprunner v2.x版本的报告 最近组内其他同学使用httprunner做接口自动化,之前没有接触过httprunner,发现httprunner相比pytest和unittest有自己 ...
- ABC343:起航
ABC343:起航 2024/3/2/22:53 有点儿晚了,简单总结一下. 前4题都很基础,一点点小思维,其中C题 边界又盲目追求刚刚好,WA了一次,总结经验,程序实际设计应该略微大于数据范围. E ...
- Linux再学!
第三篇Linux入门 一.linux基本指令 1.Linux根目录为/,后续路径用/分隔,如/home/admin 2.Linux命令 基础格式: command: 命令本身 -options:[可选 ...