第一讲:WCF介绍

时间:2021-03-03 11:17:28
代码

链接:https://pan.baidu.com/s/15Al0OwVpjMX8TRbOFB7qZg   提取码:o835


 
 
 
 
 
 
 
 
 
 
第一讲:WCF介绍
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
介绍作为Microsoft面向服务架构(SOA)平台的Windows Communication Foundation(WCF)
WCF的一些核心概念
创建,配置以及调用WCF服务
 

 
首先我们来说一下 面向服务(SOA)

软件架构的设计经历了 从面向对象程序,到面向组件程序设计,再到面向服务程序设计。

这3种方式都致力于同一个目标:封装和重用。

1.面向对象程序设计:类封装功能并提供代码重用。

2.面向组件程序设计:用以共享封装了可重用类的二进制文档。最初,这一点只是针对本机,随着COM,DCOM,和.NET Remoting的问世,才发展到分布式环境。虽然这些分布式的方法不尽相同,但是都可以实现跨进程、跨机器。 面向组件程序设计有很多局限性,但是最明显的局限是与一些特定技术的耦合过深。JAVA客户端如何调用COM组件,.NET如何调用EJB,这些局限的产生均是协议和通信格式的问题。( 解释  技术的耦合过深: 例如我们的客户端是个Net技术开发的程序,服务端是一个EJB的技术,那么就不能进行通信的。 再例如,我们的客户端是一个Java技术开发的程序,那么就无法与服务端的COM组件进行通信。 这些都是因为相互之间的协议以及通信方式的不同所导致的)

3.面向服务程序设计:解决了不同客户之间的通信,也就是不必担心彼此采用了不同的技术。(面向服务程序设计解决了面向组件的弊端,不管是什么样的客户端都可以去访问服务端的程序 )

例如下图 [ 1-01 ]

第一讲:WCF介绍

不同的客户端以及应用程序都可以访问服务端A,B,C。

当然还有其它的理解,可以通过上网去查一些资料。


 
Windows Communication Foundation
WCF是对现有Windows平台下分布式通信技术的整合。(例如Net 下 经常用到的  Com , WebService,Remoting 等分布式技术,大家试想一下,如果我们需要开发分布式的应用,那么我们就需要去学习前面提到的不同的技术,那对与我们这些苦逼程序员来说简直就是一场灾难。现在微软将这些所有分布式的技术整合,降低了我们的学习成本,做出了WCF,当然了,并不是将这些技术的简单拼凑,WCF有自己本身的机制)
 
我们看下普通设计思路 [ 1-02
第一讲:WCF介绍
客户端与业务层紧密联系,试想,如果客户端是一个Java程序,那么服务器端是一个Net程序,那这样就不能进行通信,并且技术的耦合度很高,这是传统的三层架构设计
 
我们再看下一组图片  [ 1-03 ]
第一讲:WCF介绍
 
客户层与业务逻辑层中间还有一层服务层,这样的话我们的客户层与逻辑层的通信是由服务层进行通信的,那么这样我们不管客户层与业务逻辑层之间有什么变化,或者采用什么样的技术,例如说业务层利用一些COM技术开发的而客户端是Java技术开发的,都不用去关心。这里全部交给服务层去处理,客户层去连接服务层,然后服务层去连接业务层。这样就降低了客户层与业务逻辑层的耦合度。
 

 
上面说了服务层(Service)是连接客户层与业务逻辑层的钳接点。
那么我们再说下这个WCF服务层的运行环境:
WCF的服务,如果要使这个服务能够被用户(客户端)使用,必须放到一个主机(Host,这里的主机可以认为是一个应用程序,例如 WinForm,WebForm,控制台应用程序 等等 )中进行运行,且暴露出访问点进行访问。(这里我们想象WCF的服务就是一个Web网站,而如果寄宿在控制台应用程序中,那么控制台应用程序就是我们的IIS,要访问网址就必须有响应的网站地址)
 
我们看图: [1-04]
第一讲:WCF介绍
Service(服务)运行在(寄宿)某个主机进程中,上面说了主机进程就是WinForm,WebForm,控制台应用程序 等等
 
下一步 继续看图:[ 1-05 ]
第一讲:WCF介绍
运行在(寄宿)主机进程中的服务暴露出相应的地址进行访问
 
继续下一步
 
那么现在我们开始进行访问,访问就必须有对应的客户端  [ 1-06 ]
 
第一讲:WCF介绍
 
客户端想访问服务端就必须创建相应的代理去访问服务端
 
客户端的代理请求的时候指定请求某个EndPoint (即是WCF服务暴露的访问点),所以客户端也必须配置与WCF服务端的EndPoint对应的EndPoint
 
例如去某个公司找某人,你必须先经过前台工作人员取得联系并且确认后前台带你过去。而前台就如这里的客户端代理。
 
说到这里我不得不为大家介绍 一个  名词 :寄宿
 
WCF的服务不能孤立地存在,须要寄宿于一个运行着的进程中,我们把承载WCF服务的进程称为宿主,为服务指定宿主的过程称为服务寄

宿(Service Hosting)

服务寄宿(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介绍

第二步

创建服务契约  Contracts(开始搭建了)

从功能上讲,服务契约抽象了服务提供的所有操作,而站在消息交换的角度来看,服务契约则定义了基于服务调用的消息交换过程

一般地,我们通过接口的形式定义服务契约。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;
}
}
}
 
 
 
 
第四步
通过自我寄宿的方式 进行 寄宿服务  Hosting

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 元数据