2.WCF学习--地址
一、URI(统一资源标识)
web服务可视为一种网络资源,并且可以通过一个URI来进行唯一标识。而服务通过终结点的形式发布出来,我们所说的一个服务在大部分场景中实际上指的是服务的某个终结点。终结点的核心就是一个URI,它可以作为终结点的唯一标识,还具有路由或寻址的功能。地址对应的URI不仅代表了服务所在位置,也代表了消息路由的目标地址
URI结构:
[传输协议(Scheme)]://[主机名称|域名|IP地址]:[可选端口]/[资源路径]
例如:
http://artech.com:9999/myservices/calculatorservice.svc
二、HTTP/HTTPS
HTTP全称为 HyperText Transfer Protocol(超文本传输协议),是建立在TCP/IP簇上的应用层协议。由于其简单、易用的特性,已经成为事实上的Internet标准。
1、TTTP提供简单的请求-回复消息传输方式
2、HTTP是无状态的,每次请求都是互相独立的
3、HTTP是无连接的,基于HTTP的数据传输无须事先打开连接
HTTPS全称为HypeText Transfer Protoco over Secure Socket Layer(安全超文本传输协议),它采用了SSL(Secure Socket Layer) 的HTTP。而SSL是一个进行数据加密的协议,很多安全性要求高的网站都采用HTTPS。WCF通过HTTPS实现了基于HTTP的传输安全(Transport Security)
注意:HTTP和HTTPS的URI分别使用http和https作为传输协议前缀,默认使用的端口号分别为80和443
http://artech.com/myservices/calculatorservice.svc = http://artech.com:80/myservices/calculatorservice.svc
三、Net.TCP
TCP全称为Transport Control Protocol(传输控制协议),在整个TCP/IP簇上处于核心地位。从整个协议分层结构来看,位于应用层之下,网络层(IP协议)之上。
1、TCP是基于连接的传输协议,在开始进行数据传输之前,通过客户端和服务端之间的3次“握手”创建连接;在结束传输之后,通过4此“握手”终止连接
2、TCP是有状态的,由于数据传输在一个确定的连接中进行,因此可以保持每次数据传输的状态
3、TCP支持全双工通信,一旦连接成功创建,数据就可以在两个方向上同时传输
4、TCP支持可靠通信,IP协议本身提供的数据是不可靠的,数据的可靠传输只能通过TCP来保证
注意:WCF通过NetTcpBinding支持基于TCP的传输。对于TCP的URI,其传输协议前缀为net.tcp://。默认端口号为808
四、Net.Pipe
命名管道(Named Pipes)是Windows平台及UNIX系统下实现跨进程通信(Inter Process Communication,IPC)的标准实现方式。虽然命名管道本身可以实现跨机器的通讯,但是WCF只将命名管道专门用于同一台机器的跨进程的通讯,所以基于命名管道的URI的主机名称|域名|IP地址部分只能是本机的机器名、localhost、127.0.0.1,基于命名管道的URI以net.pipe为前缀,而在基于同一台机器的不同进程间通信中,端口没有任何意义
五、Net.Msmq
消息队列(Message Queuing,也称MSMQ),是微软对消息服务领域的开创性尝试。由于消息队列采用了特殊的通信机制,因此对于改善和提高系统的可扩展性和高可用性具有重要的意义。
按照可访问行划分如下:
1、公共消息队列:公有队列的名称被注册到AD域中,所以我们无须指定队列所在的机器名称就可以访问队列。当将某个公有队列从一台机器转移到另一台机器时,访问该队列的应用可以保持不变。公有队列还可以提供基于域账号的Windows认证机制,所以对于正式发布的应用来说,通常采用公有队列
2、私有消息队列:因为公有队列需要注册到AD域中所以它只能用于域(Domain)模式下。在工作组(Work Group)模式下,只能使用私有队列。而访问私有队列需要指定包含队列所在机器名称的路径
注意:WCF下基于消息队列的URI具有net.msmq前缀。在主机名称和队列名称通过字符Private标识私有队列,而对于公有队列的URI,表示队列类型部分则不是必需的
net.msmq://artech.com/myservices(公有队列)
net.msmq://artech.com/Private/myservices(私有队列)
六、EndpointAddress
终结点在WCF应用编程接口中通过System.ServiceModel.Description.ServiceEndpoint类型表示,ServiceEndpoint核心属性如下:
public class ServiceEndpoint { public EndpointAddress Address { get; set; } //地址 public Binding Binding { get; set; } //绑定 public ContractDescription Contract { get; set; } //契约 }
表示终结点地址的Address属性是一个类型为System.ServiceModel.EndpointAddress的对象(地址核心功能:服务标识/定位、辅助寻址和服务身份标识)
public class EndpointAddress { public AddressHeaderCollection Headers { get; }//辅助寻址(地址包头列表存放一些寻址信息) public Uri Uri { get; } //服务与定位(可以是物理地址,也可以是逻辑地址) public EndpointIdentity Identity { get; } //身份标识(服务身份,被客户端用于认证服务) }
EndpointIdentity:客户端终结点通过地址的该属性表示自己希望调用服务的真实身份。调用前,服务端将自己的凭证(Windows凭证、X.509证书凭证等)提供给客户端,而客户端通过以EndpointIdentity对象代表的服务身份与凭证进行比较,来验证正在调用的服务确实是自己所希望调用的服务
七、服务端终结点地址
WCF通过ServiceHost完成宿主的寄宿,而被寄宿的服务通过添加到ServiceHost的终结点暴露出来成为可被调用寻址和调用的资源
public class ServiceHost : ServiceHostBase { public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri); }
ServiceHostBase是ServiceHost的基类
public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable { public virtual void AddServiceEndpoint(ServiceEndpoint endpoint); public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address); public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri); public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address); public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri); }
implementedContract:表示服务契约类型全名的字符串
listenUri:服务的监听地址
using (ServiceHost host = new ServiceHost(typeof(Service.CalculatorService))) { //添加终结点(一个基于WSHttpBinding绑定的终结点 终结点地址:http://127.0.0.1:3721/calculatorservice 服务契约的类型:ICalculator) host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:3721/calculatorservice"); host.AddServiceEndpoint(typeof(ICalculator), new NetTcpBinding(), "http://127.0.0.1:3721/calculatorservice"); }
添加的终结点我们可以通过他的只读属性Description获取
public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable { public ServiceDescription Description { get; } }
ServiceDescription:表示整个服务的描述(终结点列表)
public class ServiceDescription { public string Name { get; set; } public string Namespace { get; set; } public KeyedByTypeCollection<IServiceBehavior> Behaviors { get; } public string ConfigurationName { get; set; } public ServiceEndpointCollection Endpoints { get; } public Type ServiceType { get; set; } public static ServiceDescription GetService(Type serviceType); public static ServiceDescription GetService(object serviceImplementation); }
除了使用代码添加终结点还可以使用配置文件方式配置终结点
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="metadataBehavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="calculatorservice/metadata" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="metadataBehavior" name="Service.CalculatorService"> <endpoint address="calculatorservice" binding="wsHttpBinding" bindingConfiguration="" name="Service.CalculatorService" contract="Service.Interface.ICalculator" /> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:9999" /> </baseAddresses> </host> </service> </services> </system.serviceModel>
这样我们就不需要再使用代码来添加终结点了
using (ServiceHost host = new ServiceHost(typeof(CalculatorService))) { host.Opened += delegate { Console.WriteLine("CalculatorService 启动,按任意键终止服务!"); }; host.Open();//开启服务 Console.Read(); }
注意:WCF在进行基地址和相对地址匹配的时候,会根据终结点的绑定类型从基地址列表中获取与传输协议相匹配的基地址
地址终结点共享:一般情况下,服务实现一个单一的契约接口,所以服务的所有终结点共享相同的契约。既然这些终结点具有相同的契约,他们的地址就不可能相同。但是对于一个实现了多个服务契约接口的服务来说,多个基于不同契约的终结点就可以共享相同的地址,但是必须共享相同的绑定对象实例(这个地方有待完善,没有验证。。。)
例如我们有两个契约接口 ICalculator1和ICalculator2 服务契约的实现类 CalculatorService
using (ServiceHost host = new ServiceHost(typeof(Service.CalculatorService))) { WSHttpBinding binding = new WSHttpBinding(); //添加终结点(一个基于WSHttpBinding绑定的终结点 终结点地址:http://127.0.0.1:3721/calculatorservice 服务契约的类型:ICalculator) host.AddServiceEndpoint(typeof(ICalculator1), binding , "http://127.0.0.1:3721/calculatorservice"); host.AddServiceEndpoint(typeof(ICalculator2), binding , "http://127.0.0.1:3721/calculatorservice"); }