此文章转载自Sharping's Nonsense的http://www.sharping.net/CommentView,guid,9592435d-5496-45f4-a80e-d0a121d454cf.aspx
引言:如果你是一个热爱技术的人,相信看了前面几篇文章后已经迫不及待的去写代码了吧,如果你是一个乐于创新技的术追求者,你一定发现了Rremoting中的一些问题是我没讲到的。细节,又见细节,今天要探讨的就是Remoting中的细节之一——多通道注册的实现。
常常有这样的需求,我们需要在两个不同的通道完成两种不同的服务,那么回顾一下前面讲的内容,注册通道:
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);
那么多通道呢?通常的思维模式应该是这样实现:
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);
TcpChannel channel = new TcpChannel(9999);
ChannelServices.RegisterChannel(channel);
结果出忽意料,这样的语句在运行时检查时候会出问题,编译可以通过。
通过查阅相关资料,发现,Remoting用来区分通道的标识是来自TcpChannel的Name属性而并不是端口号,也就是说每个通道必须有不同的Name属性,这样理论上可以在创建TcpChannel实例后对其Name属性赋值再进行注册。可是在MSDN上查看TcpChannel类的Name属性原形发现这个Name属性只有get访问器,也就是说他是只读的。晕了吧~~~看来问题还要继续往上追溯。
通过查阅MSDN可以发现,其实TcpChannel有三种重载构造方法。其中一种就是我们上面的形式,还有不指定端口的,最后还有一种参数很多,我们来看看他的原形:
public TcpChannel(
IDictionary properties,
IClientChannelSinkProvider clientSinkProvider,
IServerChannelSinkProvider serverSinkProvider
);
参数说明:
properties
保存当前信道配置信息的信道属性的 IDictionary。
clientSinkProvider
IClientChannelSinkProvider,它为远程处理消息所流过的基础 TcpClientChannel 创建客户端信道 接收。
serverSinkProvider
IServerChannelSinkProvider,它为远程处理消息所流过的基础 TcpServerChannel 创建服务器信道接收。
如果你有好的编程习惯的话会知道,I打头的类型是接口类型。通过查阅资料可以知道,在.NET里Hashtable类是实现了IDictionary接口的,BinaryClientFormatterSinkProvider类实现了IClientChannelSinkProvider 接口,而 BinaryServerFormatterSinkProvider类实现了IServerChannelSinkProvider接口,至于后面两个类,可以使用构造默认构造方法来创建实例,把这两个实例做为后面两个参数即可,因为他们只是提供二进制格式化接收程序,关于这方面细节可以查阅MSDN,通道的属性主要依靠第一个参数IDictionary来区别。
我们来看看Hashtable类,在MSDN里是这样描述他的:表示键/值对的集合,这些键/值对根据键的哈希代码进行组织。键?值?听起来是不是很象数据库里的术语,对,其实他们在某种程度上是同一概念。所谓键,可以理解成字段,值可以理解成这个字段的Value(又想不到词了,只能用E文解释中文)。其实他的实现原理是Hashtable类为我们提供了一个返回类型和参数类型都是string类型的索引器,这样可以自己创建一些键/值对,那么想一想,TcpChannel类的Name属性是不是这样对应的一个Name对应一个字符串(string类型),这里Name就是键,而这个字符串就是值。另外TcpChanne类当然还少不了他的port属性。那么这样创建Hashtable类的实例:
Hashtable instance = new Hashtable();
tcpProp["name"] = "tcp9090";
tcpProp["port"] = 9090;
上面说过 Hashtable 类实现了IDictionary接口,那么程序就因该是这样:
IDictionary iChannel = new Hashtable();
tcpProp["name"] = "tcp9090";
tcpProp["port"] = 9090;
这样就构造出了第一个参数,那么多通道问题就可以解决了。我们来看完整代码,注册一个http通道和一个tcp通道.
注册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);
问题到这里算解决了,但是,我觉得有必要研究清楚最开始我们的代码出了什么问题。
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);
这样的注册方式只改变端口参数为什么无法实现多通道呢?原来 new TcpChannel(8080)方式创建的TcpChannel实例的Name属性默认为"tcp",而以new HttpChannel(8080)方式创建的HttpChannel实例其Name属性为"http",那么两个通道名称都是"tcp"这样就违反了通道名作为区别通道标识的原则,当然就失败了。清楚了,文章完。
篇外: 有句话说的好:"细节决定成败!" 细节其实往往是学习中最难掌握的,如果说高手和菜鸟的区别主要在编程思想的话,那么可以这么说,高手和菜鸟在编程技术上的区别主要是在细节的掌握上,掌握细节,那么你也算个“小高手”。