【WCF】基址与默认终结点
五月份的时候,有位老友给老周提了个建议:希望老周写一写WCF的文章。其实老周以前是写过WCF的文章的,只是不是写在这个博客里,老周并不打算把X年前的博客导进来,要写的话,重新写吧。毕竟,那个时候写的文章是比较肤浅。
根据老周有限的记忆,WPF、WCF、WF、WIF等已经问世有十个年头了,老周耍.net已经十几个年头了吧,因为.net好玩、耐玩,所以一玩十来年,一直享受着优游之乐,如同当年庄子在濠梁之上所说的一样。
顺便跟各位在校的同胞们说一下,.net已不是什么新技术了,如果你认为它是新技术,表明你与你的学校实在太落后了,落后得无法用文字来形容。像WPF、WCF、WF、MEF等东东,包括现在的UWP、.net core等新玩意儿,你只能依靠自学,不走自学之路,你很难有所收获的。你一定要相信,你有自学成才的能力,想想,像老周这种笨蛋都可以完全依靠自学,你一定能的。而且,还要告诉你,老周自学的不仅仅是编程,老周自学的领域覆盖文、理,贯通天地人神鬼,涉猎古今……老周向来是爱好广,兴趣广。
那么,为啥强调自学呢?因为当今中国的大学,计算机教学水平,普通低下,低到惨不忍睹的程度。许多大学计算机老师,一辈子只见过两样东西——C/C++ 和 Java,除此二者之外,他们都不懂,教材照样年年用着上世纪90年代的书,学习工具停留在VS 6.0,所学内容严重脱离实际应用。老周一直想不明白,那些什么教授啊老师啊,研究了几十年,就研究出这点水平,也不知道他们平时都干什么去了,可能去研究妹子去,看妹子的眼光比看技术的眼光还准。
哪怕是C/C++,如果你问老师:函数传参中,int* x 和 const int& x 有什么不同,或者问 int const* x 是啥、exten "C"是什么东东,估计一些老师的反应是:这个,这个嘛,这个……现在别问这些,打基础要紧。
所以,你看看,不依靠自学,你能学到东西吗?
好了,转回咱们今天的主题。今天咱们聊聊WCF的一个基础知识——基址和默认终结点。
基址,从base address翻译过来,可能英文名字更好理解,就是给服务指定一个基础URI,而随后向服务添加的终结点的地址可以基于这个地址,当然了,添加的终结点也可以指定绝对URI。
例如,基址为http://mm.net/images/,某个终结点的相对地址为face,于是,与基址一组合,终结点的完整地址就是 http://mm.net/images/face。
再比如,基址net.tcp://192.168.1.20:999/,某个终结点的相对URI为 pay,那么,与基址组合后,终结点的完整地址为:net.tcp://192.168.1.20:999/pay。
在非IIS上运行时,比如在一个exe中承载服务,会用到ServiceHost类,实例化该host时,需要指定一个Type,它表示服务类的类型信息,至于基址,是可选的。
如果不指定基址,那么,添加的终结点就必须指定绝对URI;如果有了基址,那么添加的终结点既可以用相对URI,也可以用绝对URI。
在定义基址时,有一点要注意,相同的协议不能添加多次,比如:
http://someone:1010/sv
已经添加到基址列表中,你就不能再添加http://someone:1012/orders了,因为http协议的地址已经添加过了。不过,运行在IIS中的WCF服务可以通过以下配置来解决:
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
在windows进程中host的服务就不可以了,所以,你要记住了,像下面这样指定基址是会报错的。
<services> <service name="sv"> <host> <baseAddresses> <add baseAddress="http://localhost:900/service"/> <add baseAddress="http://localhost:1000/services/mytest"/> <add baseAddress="http://localhost:1100/wcfservices/mytestfun"/> </baseAddresses> </host> </service> </services>
如果不是同一协议,是可以指定多个基址的,比如:
<host> <baseAddresses> <add baseAddress="http://localhost:900/"/> <add baseAddress="net.tcp://localhost:3500/services"/> </baseAddresses> </host>
一个是http的,一个是netTcp的,所以这两个基址是允许的。
基址介绍完了,再看看默认终结点,要使用默认终结点,一定要添加基址。所谓默认终结点,就是你不用手动去添加终结点,只要向ServiceHost指定了基址和服务类的类型,那么,运行时会根据服务类所实现的协定,自动添加终结点,终结点的地址就是基址。
比如,假设服务类实现了一个服务协定接口,并且指定了以下两个基址:
<add baseAddress="http://localhost:6000/"/> <add baseAddress="net.tcp://localhost:6100"/>
服务会添加两个默认终结点,针对http协议的终结点将使用basicHttpBinding,针对netTcp协议的终结点会使用NetTcpBinding,并且两个终结点的Contract都是同一个协定——服务实现的那个协定。
可以加入以下代码来检测一下:
ServiceHost host = new ServiceHost(typeof(MyService)); host.Open(); foreach(ServiceEndpoint ep in host.Description.Endpoints) { Console.WriteLine($"地址:{ep.Address.Uri.AbsoluteUri},binding类型:{ep.Binding.GetType().Name}"); } Console.WriteLine("按任意键退出。"); Console.Read(); host.Close();
然后,你会看到以下输出。
上面的情况是我只实现了一个服务协定,那么,如果服务类实现了多个服务协定接口呢?
来,试试看。
[ServiceContract()] public interface IService { [OperationContract] void GoodBoy(string name); } [ServiceContract] public interface IService2 { [OperationContract] void GoodGirl(string name); }
睁大眼睛看好了,这是两个服务协定接口,然后,我们用一个服务类同时实现这两个接口。
class MyService : IService, IService2 { public void GoodBoy(string name) { // ................................. } public void GoodGirl(string name) { // ................................. } }
好了,现在你知道,服务类MyService实现了两个服务协定,那么,运行时添加的默认终结点会有几个呢?不急,真相就在下面。
把上面的输出代码改一下,让其输出协定的类型名称。
foreach (ServiceEndpoint ep in host.Description.Endpoints) { Console.WriteLine($"地址:{ep.Address.Uri.AbsoluteUri},binding类型:{ep.Binding.GetType().Name},协定名:{ep.Contract.ContractType.Name}"); }
运行后,看到如下内容。
为啥会变成4个终结点了?
首先,我们知道,有两个基址,分别是http和nettcp协议。
再者,服务实现了两个协定。
一个终结点由地址、绑定、协定三个主要要素组成,非主要要素可能会有Behavior、地址头等。也就是说,一个终结点只能指定一个服务协定,一个终结点只能指定一个绑定,一个终结点也只能有一个地址。
现在你明白了吧,两个协定,两个地址,所以终结点的个数 = 2 × 2,就是4个了。
依此类推,两个基址(不同协议)固定,如果服务类实现了3个协定接口,那么,运行时添加的默认终结点就有6个。
好了,今天的话题就讨论到此了,不难吧。