链接:https://pan.baidu.com/s/15Al0OwVpjMX8TRbOFB7qZg 提取码:o835
软件架构的设计经历了 从面向对象程序,到面向组件程序设计,再到面向服务程序设计。
这3种方式都致力于同一个目标:封装和重用。
1.面向对象程序设计:类封装功能并提供代码重用。
2.面向组件程序设计:用以共享封装了可重用类的二进制文档。最初,这一点只是针对本机,随着COM,DCOM,和.NET Remoting的问世,才发展到分布式环境。虽然这些分布式的方法不尽相同,但是都可以实现跨进程、跨机器。 面向组件程序设计有很多局限性,但是最明显的局限是与一些特定技术的耦合过深。JAVA客户端如何调用COM组件,.NET如何调用EJB,这些局限的产生均是协议和通信格式的问题。( 解释 技术的耦合过深: 例如我们的客户端是个Net技术开发的程序,服务端是一个EJB的技术,那么就不能进行通信的。 再例如,我们的客户端是一个Java技术开发的程序,那么就无法与服务端的COM组件进行通信。 这些都是因为相互之间的协议以及通信方式的不同所导致的)
3.面向服务程序设计:解决了不同客户之间的通信,也就是不必担心彼此采用了不同的技术。(面向服务程序设计解决了面向组件的弊端,不管是什么样的客户端都可以去访问服务端的程序 )
例如下图 [ 1-01 ]
不同的客户端以及应用程序都可以访问服务端A,B,C。
当然还有其它的理解,可以通过上网去查一些资料。
宿(Service Hosting)
1.自我寄宿(self-Hosting)
2.IIS寄宿(寄宿进程为IIS的工作进程W3wp.exe)
我们来看一个Demo ,在这个例子中,我们将实现一个简单的计算服务。客户端和服务通过运行在同一台机器上不同进程模拟。(和传统的分布式通信框架一样,WCF本质上提供一个跨进程、跨机器以致网路的服务调用)
这个例子可以看到 寄宿的 第一种情况 自我寄宿
第一步
先看构建的整个解决方案(说了这是只是先去看不去搭建):
1: Contracts:一个类库项目,定义服务契约(Service Contract)
2: Services:一个类库项目,提供对WCF服务的实现
3: Hosting:一个控制台应用,实现对定义在 Services项目中的服务的寄宿
4: Client:一个控制台应用模拟服务的客户端
通过VS创建一个空白的解决方案,添加4个项目。项目的类型,承载的功能和相互引用的关系如下,整个项目的结构如下:
1:Contracts:一个类库项目,定义服务契约(Service Contract), 引用System.ServiceMode程序集(WCF框架的绝大部分实现和API定义在该程序集中)
2:Services:一个类库项目,提供对WCF服务的实现。定义在该项目中的所有WCF服务实现了定义在Contracts中相应的服务契约,所以Services具有对Contracts项目的引用。
3:Hosting:一个控制台应用,实现对定义在Services项目中的服务的寄宿,该项目需要同时应用Contracts和Services两个项目和System.ServiceMode程序集。
4:Client:一个控制台应用模拟服务的客户端,该项目引用System.ServiceMode程序集。
[ 1-07 ]
第二步
从功能上讲,服务契约抽象了服务提供的所有操作,而站在消息交换的角度来看,服务契约则定义了基于服务调用的消息交换过程
一般地,我们通过接口的形式定义服务契约。WCF广泛采用基于自定义特性的声明编程模式,我们通过在接口上应用System.ServiceModel.ServiceContractAttribute特性将一个接口定义成服务契约。在应用ServiceContractAttribute特性的同时,还可以指定服务契约的名称和命名空间。通过应用ServiceContractAttribute特性将接口定义成服务契约之后,接口的方法成员并不能自动成为服务的操作。在此方面,WCF采用的是显示选择的策略:我们须要在相应的操作方法上面显示地应用OperationContractAttribute特性。
(上面的两句话什么意思呢?我们这里看一遍理解个大概不去深究,将整个Demo看完之后回来再看一遍上面的两句话就理解了,也不去管服务契约(契约 分 服务契约、数据契约、消息契约、错误契约),因为后面我们会说到,这里就把契约它看成程序中定义的接口就成了)
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks; namespace Contracts
{ [ServiceContract]
public interface ICalculator
{
[OperationContract]
double Add(double x, double y);
}
}
第三步
创建服务 Services
当服务契约创建成功时,我们需要通过实现服务契约来创建具体的WCF服务。WCF服务CalculatorService定义在Services项目中,实现了服务契约接口ICalculator,实现了所有的服务操作.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Contracts; namespace Services
{
public class CalculatorService : ICalculator
{
public double Add(double x, double y)
{
return x + y;
}
}
}
WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程。WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段。
1:地址(Address):地址决定了服务的位置,服务寻址的问题。
2:绑定(Binding):实现了通信的所有的细节,包括网络传输,消息编码等。
3:契约(Contract):契约是对服务操作的抽象,也是对消息交换模式以及消息结构的定义。
Endpoint=ABC ( Address 、Binding、Contract) 后面说的 EndPoint 就是指配置 A,B,C
服务寄宿的目的就是开启一个进程,为WCF服务提供一个运行的环境
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using Contracts;
using Services; namespace Hosting
{
class Program
{
static void Main(string[] args)
{
//首先提供一个主机进程,实际上就是完成寄宿的主机( CalculatorService 完成了对契约的实现,所以我们这里就寄宿就寄宿它了 )
using (var host = new ServiceHost(typeof(CalculatorService)))
{
/*
然后 当我们把寄宿后,就要完成我们的EndPoint(终结点) 了,之前提过EndPoint=A,B,C 那么这里就要绑定了
之前的图片 [ 1-05 ] 已经展示 过这个 EndPoint
这里的 WSHttpBinding 就是 指定 HTTP 协议 当然还有很多种,后面说到
添加终结点 首先 C,然后 B 最后 A
*/
host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:6666/calculatorservice"); //这里检测 元数据 为不为空 这里的元数据也不管是什么东西,下面我们说到
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
{
//也就是说这里我们是以元数据的形式发布出去进行客户端与服务器的交互
//控制服务元数据和相关信息的发布
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;//是否可以通过 HTTP Get 形式 去访问
// 元数据的地址,可以通过这个地址 在 浏览器中 进行访问
behavior.HttpGetUrl = new Uri("http://127.0.0.1:6666/calculatorservice/metadata");
//添加到元数据中去
host.Description.Behaviors.Add(behavior);
}
//指定一个事件,当服务启动之后 需要做什么,这里指定一个委托,在 Open 成功后,就执行这里的事件
host.Opened += (sender, eventArgs) => Console.WriteLine("服务已经启动,按任何按钮停止");
//开启服务
host.Open();
Console.Read();
}
}
}
}
到这里 我们服务器 部分 就完成了
接下来我们调试一下:
[ 1-08 ]
图片上传不成功,博客园不允许上传10M以上的图片。这么多年,博客园都没有好好更新下,我觉得挺让人窝火的。
好,非常成功。
我们直接在 浏览器中去访问: http://127.0.0.1:6666/calculatorservice/metadata
呈现出来的
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:tns="http://tempuri.org/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://tempuri.org/" name="CalculatorService">
<wsp:Policy wsu:Id="WSHttpBinding_ICalculator_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:ProtectionToken>
<wsp:Policy>
<sp:SecureConversationToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:RequireDerivedKeys/>
<sp:BootstrapPolicy>
<wsp:Policy>
<sp:SignedParts>
<sp:Body/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="To"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="From"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="FaultTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="ReplyTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="MessageID"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="RelatesTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="Action"/>
</sp:SignedParts>
<sp:EncryptedParts>
<sp:Body/>
</sp:EncryptedParts>
<sp:SymmetricBinding>
<wsp:Policy>
<sp:ProtectionToken>
<wsp:Policy>
<sp:SpnegoContextToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:RequireDerivedKeys/>
</wsp:Policy>
</sp:SpnegoContextToken>
</wsp:Policy>
</sp:ProtectionToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:EncryptSignature/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:SymmetricBinding>
<sp:Wss11>
<wsp:Policy/>
</sp:Wss11>
<sp:Trust10>
<wsp:Policy>
<sp:MustSupportIssuedTokens/>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
</wsp:Policy>
</sp:Trust10>
</wsp:Policy>
</sp:BootstrapPolicy>
</wsp:Policy>
</sp:SecureConversationToken>
</wsp:Policy>
</sp:ProtectionToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:EncryptSignature/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:SymmetricBinding>
<sp:Wss11 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy/>
</sp:Wss11>
<sp:Trust10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:MustSupportIssuedTokens/>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
</wsp:Policy>
</sp:Trust10>
<wsaw:UsingAddressing/>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="WSHttpBinding_ICalculator_Add_Input_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="To"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="From"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="FaultTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="ReplyTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="MessageID"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="RelatesTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="Action"/>
</sp:SignedParts>
<sp:EncryptedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
</sp:EncryptedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="WSHttpBinding_ICalculator_Add_output_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="To"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="From"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="FaultTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="ReplyTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="MessageID"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="RelatesTo"/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing" Name="Action"/>
</sp:SignedParts>
<sp:EncryptedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
</sp:EncryptedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsdl:types>
<xsd:schema targetNamespace="http://tempuri.org/Imports">
<xsd:import namespace="http://tempuri.org/" schemaLocation="http://127.0.0.1:6666/calculatorservice/metadata?xsd=xsd0"/>
<xsd:import namespace="http://schemas.microsoft.com/2003/10/Serialization/" schemaLocation="http://127.0.0.1:6666/calculatorservice/metadata?xsd=xsd1"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="ICalculator_Add_InputMessage">
<wsdl:part name="parameters" element="tns:Add"/>
</wsdl:message>
<wsdl:message name="ICalculator_Add_OutputMessage">
<wsdl:part name="parameters" element="tns:AddResponse"/>
</wsdl:message>
<wsdl:portType name="ICalculator">
<wsdl:operation name="Add">
<wsdl:input message="tns:ICalculator_Add_InputMessage" wsaw:Action="http://tempuri.org/ICalculator/Add"/>
<wsdl:output message="tns:ICalculator_Add_OutputMessage" wsaw:Action="http://tempuri.org/ICalculator/AddResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="WSHttpBinding_ICalculator" type="tns:ICalculator">
<wsp:PolicyReference URI="#WSHttpBinding_ICalculator_policy"/>
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="Add">
<soap12:operation style="document" soapAction="http://tempuri.org/ICalculator/Add"/>
<wsdl:input>
<wsp:PolicyReference URI="#WSHttpBinding_ICalculator_Add_Input_policy"/>
<soap12:body use="literal"/>
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference URI="#WSHttpBinding_ICalculator_Add_output_policy"/>
<soap12:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalculatorService">
<wsdl:port name="WSHttpBinding_ICalculator" binding="tns:WSHttpBinding_ICalculator">
<soap12:address location="http://127.0.0.1:6666/calculatorservice"/>
<wsa10:EndpointReference>
<wsa10:Address>http://127.0.0.1:6666/calculatorservice</wsa10:Address>
<Identity xmlns="http://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
<Upn>DESKTOP-K3KFPAM\xujunheng</Upn>
</Identity>
</wsa10:EndpointReference>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
其实就是 WSDL 元数据