本节将介绍URI、端口共享、请求监听和消息分发等概念。正如之前所说的,WCF服务是通过终结点EndPoint发布,而终结点由地址、绑定和契约三要素组成,其中地址用于定位服务,并提供额外的寻址信息和认证信息。既然是服务定位,首先引入URI的概念,URI的全称为Uniform Resource Identifier统一资源标识,其形式是,[Schema传输协议]://[主机名|域名|IP地址]:[端口号]/[资源路经],其中支持的协议类型如下表所示。
协议类型 |
解释 |
HTTP/HTTP |
前者是互联网时代的核心--超文本传输协议,其是建立在TCP/IP协议簇上应用层协议。特点无状态、无连接、提供简单请求-回复消息传输方式;后者是采用了SSL(TLS)的HTTP,提供数据加密,实际上,大部分主流网站已实现全站HTTPS。 |
Net.TCP |
TCP全称传输控制协议,属于传输层协议,基于网络层IP协议,是应用层HTTP协议的基础。其特点是有状态、支持全双工、支持可靠通信,其是基于连接的协议,在数据传输前通过3次"握手"创建连接,在传输结束后,通过4次"握手"终止连接。 |
Net.Pipe |
命名管道是Windows等操作系统实现跨进程通信(Inter Process Communication, IPC)的标准实现方式,虽然命名管道本身可以跨机器通信,不过WCF中的命名管道专注于同一台机器中的跨进程通信,因此其主机名为localhost,此外由于基于同一台机器,端口变得没有意义。 |
Net.Msmq |
消息队列提供了支持离线的通信机制,其包括公共消息队列和私有消息队列两种方式,前者需要注册到AD域中。此外,除了存储业务数据消息的普通队列之外,还有存储消息拷贝的日志队列、存储确认消息的管理队列、存储回复消息的回复队列和存储死信消息的死信队列等。 其URI格式为: net.msmq://sory.com/private/xxxservice |
之前提及的核心概念终结点在WCF中,通过System.ServiceModel.Description.ServiceEndpoint类表示,其包括Address、Binding、Contract三个核心属性。其中的Address是EndpointAddress的实现类,其包含Uri、Headers、Identity三个属性,Uri即是服务的唯一标识,也是服务的目标地址,且这个地址可以使物理的,也可以是逻辑的。这儿的Headers其实就是SOAP消息中的消息头(类似于Http协议的,也包括消息头和消息体,前者主要提供一些控制信息,后者存放数据部分),它默认通过DataContractSerializer进行序列化和反序列化,最终转化为SOAP消息的MessageHeader,相应配置如下所示,添加了服务端消息头后,在客户端也需要增加相应消息头,否则会被地址过滤器给过滤掉(之后的客户端通过ChannelFactory调用服务的示例中可以看到)。
1 <endpoint address="http://127.0.0.1:9901/addservice" binding="wsHttpBinding" contract="Sory.Entertainment.WCF.IAddService"> 2 <headers> 3 <authentication xmlns="http://www.sory.com/">{12345678}</authentication> 4 </headers> 5 </endpoint>
补充一点的是,可以通过将服务的ServiceBehavior特性中的AddressFilterModel属性设置为Any,跳过消息头的检验。
在基础概念一节的代码示例中,可以看到WCF通过ServiceHost完成服务寄宿,其中通过AddServiceEndpoint实现终结点的添加,当然也可以通过配置文件的方式添加终结点,在配置文件的<system.serviceModel>模块的<service>子节点中添加<endpoint>,并补全address、binding、contract属性,注意在IIS寄宿的情况下,无需提供address,因为.svc文件的地址就是服务的地址。同时,可以通过ServiceHost的Description属性(.NET中习惯使用Description获取元数据相关信息,无论是哪一种框架)获取终结点和服务行为的相关信息。
此外,除了使用绝对地址来指定某个服务的终结点地址外,还可以通过"基地址+相对地址"的方式,其配置形式如下,需要注意一种类型的协议只能有一个基地址,并且当一个服务实现类同时实现了多个服务接口时,该终结点地址可以共享。
1 <service name="XXX" behaviorConfiguration="XXX"> 2 <host> 3 <baseAddresses> 4 <add baseAddress="net.tcp://127.0.0.1/baseservice"/> 5 </baseAddresses> 6 </host> 7 </service>
客户端通过服务代理实现对服务的调用,包括两种方式:通过服务引用或者借助SvcUtil.exe工具来生成服务代理类,该生成类继承自ClientBase<TChannel>;直接通过ChannelFactory<TChannel>创建服务代理。前者比较简单,只需要在<system.serviceModel>的子节点<client>中添加对应的<endpoint>节点,然后直接生成的对应的Client类即可,后者如下所示。
1 var uri = new Uri("http://127.0.0.1:9901/addservice"); 2 var header = AddressHeader.CreateAddressHeader("authentication", "http://www.sory.com/", "{12345678}"); 3 var address = new EndpointAddress(uri, header); 4 var binding = new WSHttpBinding(); 5 var contract = ContractDescription.GetContract(typeof(IAddService)); 6 7 var endpoint = new ServiceEndpoint(contract, binding, address); 8 using (var factory = new ChannelFactory<IAddService>(endpoint)) 9 { 10 var channel = factory.CreateChannel(); 11 var result = channel.Add(new CompositeType { PartA = 1, PartB = "Hello, " }, new CompositeType { PartA = 2, PartB = "World!" }); 12 Console.WriteLine(string.Format("PartA: {0}, PartB: {1}", result.PartA, result.PartB)); 13 }
-
端口共享
在Windows系统,为了安全,常常只开发少量端口,当有大量应用需要使用不同端口时,会显得捉襟见肘,因此多个应用共享同一个端口显得很有必要。对于Http/Https协议来说,由于其可以通过IIS来管理应用,其自身通过HTTP.SYS已经实现了80|443端口的共享。而对于TCP协议来说,其通过一个Windows服务(名称为Net.Tcp Port Sharing Service)来管理,可以通过如下方式实现其共享。
1 <bindings> 2 <netTcpBinding> 3 <binding name="portSharingBinding" portSharingEnabled="true"></binding> 4 </netTcpBinding> 5 </bindings>
-
逻辑地址和物理地址
之前在EndpointAddress中提及的Uri属性表示服务的逻辑地址,而物理地址对于服务端来说是监听地址,对于客户端来说是消息真正发送的目标地址。默认情况下,两个地址是统一的,但在需要中介进行消息转发的场景下,需要将两者分离。
对于服务端,可以设置终结点的ListenUri的属性和ListenUriMode属性(包括Explicit和Unique,前者严格使用ListenUri作为最终的监听地址,后者将通过不同的策略保证监听地址的唯一性,如针对端口共享的情况,将在默认Uri后加GUID以作识别),共同完成该需求,示例如下。
示例如下。
<endpoint address="http://127.0.0.1:9901/addservice" listenUri="http://127.0.0.1:9900/addservice" listenUriMode="Unique" …/>
对于客户端,需要借助ClientViaBehavior这一终结点行为来实现,示例如下。
1 <behaviors> 2 <endpointBehaviors> 3 <behavior name="clientViaBehavior"> 4 <clientVia viaUri="http://127.0.0.1:9900/addservice"/> 5 </behavior> 6 </endpointBehaviors> 7 </behaviors> 8 <client > 9 <endpoint behaviorConfiguration="clientViaBehavior"></endpoint> 10 </client>
补充:行为这个概念在WCF中非常的重要,很多的功能都是通过相应的行为实现的,接下来进行简要介绍。如果说契约是客户端和服务端达成的某种共识,是双边协议,而行为则是客户端或服务端在本地实现某个功能的一种方式,是一种单边行为。WCF提供了4种类型的行为,包括服务行为、契约行为、终结点行为和操作行为,它们一般可以通过特性或者配置文件的方式进行设置。
-
请求监听和消息分发
这部分内容涉及到整个WCF服务端的架构,下图展示了一个最简单的请求分发过程。
在整个消息监听和分发体系中,信道分发器和终结点分发器是两个核心的对象,前者负责请求监听、消息接收并通过消息筛选器选择正确的终结点,后者完成消息的处理。终结点分发器具有两个消息消息筛选器,分别是AddressFilter和ContractFilter,均是MessageFilter类型,前者对应的AddressFilterMode包含Exact、Prefix、Any三种枚举类型。WCF提供6种典型的消息筛选器,包括:ActionMessageFilter,判断请求消息(SOAP)的<Action>报头是否和终结点契约中任意操作的Action属性相匹配(Match);EndpointAddressMessageFilter判断<To>报头是否和终结点地址相匹配;MatchAllMessageFilter,表示全匹配;以及不常用的XPathMessageFilter、MatchNoneMeesageFilter和PrefixEndpointAddressMessageFilter。