WCF入门
公司用WCF开发即时通讯软件已有三年多,下面总结一下工作中的用到的知识。
.net3.0带来了一种专门用来构建分布式系统的API-WCF(Windows Communication Foundation)。与你过去所使用的其他分布式AIP(DCOM、.NET远程处理、XML Web服务等)有所不同,WCF提供了统一的、可扩展的单一模型来使用以前多个分布式技术。
各种分布式计算API
(1)DCOM的作用
在.net平台发布之前,DCOM(分布式组件对象模型)是微软相关开发人员可以选择的远程API。使用DCOM,我们可以使用COM对象、系统注册表(以及大量体力劳动)来构建分布式系统。DCOM的一个优势是运行组件的位置透明性。简单来说,可以不通过硬编码远程对象物理位置地址来为客户端软件编程。不管远程对象是在相同的机器上还是在二级网络计算机上,代码还是能保持不变,因为真实路径记录在外部的系统注册表中。
虽然dcom确实有一定成功,但是实际上它是windows相关的api。即使很多其它操作系统支持dcom,dcom本身并没有提供一个结构用于构建包含多个操作系统的复杂解决方案或促进多个不同架构的数据分享。总的来说,dcom非常适用于内部的应用程序开发,因为把com类型向公司防火墙外部公开会遇到很多额外的因难。随首.net平台的发布,dcom马上就变成了遗留编程模型,如果我们不是在维护遗留的dcom系统,就可以认为这项技术是不推荐的。
(2)COM+/企业服务的作用
dcom只是定义了一种在两个基于com的软件之间的创建通信通道的方式。为了完善用于构建功能丰富的分布式计算解决方案,微软最后发布了微软事务处理服务器(MTS),它在之后的发布中被命名为com+。
自从.net平台第一次发布以来,基类库就提供了一个叫System.EnterpriseServices的命名空间。在这里,.net程序员可以构建能安装到com+运行库的托管类库,这样就能像传统的com+相关的com服务器一样访问一组服务。在任何一种情况下,一旦com+相关类库安装到了com+运行库中,它就被称为服务组件。
com+提供了许多服务组可以利用的特性,包括事务管理,对象生命周期管理,池服务,基于角色的安全系统,松耦合的事件模型等。虽然com+/企业服务至今仍在使用,但是这个技术只是windows的解决方案,并且最适用于内部应用程序开发或作为被前端间接操作的后台服务。
(3)MSMQ的作用
msmq(微软消息队列)API可以来构建需要确保消息数据在网络上可靠传送的分布式系统。我们知道,任何分布式系统都存在网络服务器停机,数据库断开连接或由于各种原因引起的连接丢失等风险。此外,许多应用程序需要以这样一种形式来构建,那就是保存之后会传递的数据(叫做队列数据)。
不管我们使用哪种编程模型来和msmq运行库进行交互,最终结果确保了应用程序可以可靠并及时地传递数据。和com+相似,msmq仍然是在windows操作系统上构建分布式软件的结构的一部分。
(4).NET远程处理的作用
之前提过,随着.net平台的发布,dcom很快就成为了遗留分布式api。.net基类库附带了.net远程处理层(用System.Runtime.Remoting命名空间表示)来代替其地位。如果所用应程程序都是.net平台下,这个api就允许多个计算机来分布对象。
由于这个api只能用于.net,我们可以获得很多性能优势,因为数据可以以精简的二进制格式进行编码,并且在定义参数和返回值的时候可以使用公共类型系统(CTS)。虽然可以使用.net远程处理来构建跨越多个操作系统(通过Mono)的分布式系统,但是仍然不能和其它编程架构(如j2ee)进行直接互操作。
(5)XML Web服务的作用
xml web 服务提供了最直接的方式,使我们可以将远程对象的服务向任何操作系统和任何编程模型公开。和传统的基于浏览器的Web应用程序不同,web服务只是通过标准web协议公开远程组件功能的一种方式。自.net最初发布以来,System.Web.Services命名空间就支持程序员构建和消费xml web服务。基实,在很多情况下,构建功能完整的web服务并不比在希望提供访问的公共方法上应[WebMethod]标签复杂。
(6)命名管道、套接字和P2P
从dcom、.net远程处理、web服务、com+以及msmq中选择还不够挑战性,分布式api列表还将继续。程序还可以使用其它进程间的通信api,如命名管道、套接字和对等(p2p)服务。这些底层的api通常提供了更好的性能(特别是对相同lan中的机器来说),然而,对于面向外部的应用程序来说,使用这些api就更复杂了。如果我们要构构建的分布式系统包含的一组应用程序在同样的物理机器上运行,就可以通过System.IP.Pipes命名空间使用命名管道aip。这个方法可以提供绝对快速的方式在相同计算机的应用程序之间传数据。
同样,如果我们要构建的应用程序需要完全控制如何获得和维护网络访问的话,套接字和p2p功能可以分别通过使用System.Net.Socket和System.Net.PeerToPeer命名空间来实现。
WCF的作用
从之前的一些内容中你已经发现,这么多分布式技术使得我们很难选择合适的。更复杂的是某些技术提供的功能还有重叠(最常见的就是事务和安全)。
即使.net开发人员已经为手头的任务选择了看上去“正确”的技术,但是构建、维护和配置这样一个程序也很复杂。每个api都有自已的编程模型、独有的配置工具等。
WCF是.net 3.0引入的分布式计算工具包,它把之前的这些独立的分布式技术整合到了主要由System.ServiceModel命名空间表示的简化api中。使用wcf,我们就可以使用大量的技术来将服务公开给调用者。例如,如果要构建一个内部的应用程,所有计算机都是基于windows的,就可以使用各种TCP协议来确保最快的性能。相同的服务也完全可以使用基于XML Web有务协议进行公开,使得外部调用者可以利用其功能,而无需考虑编程语言和操作系统。
由于WCF允许我们为工作选择正确的协议(使用通用的编程模型),我们可以很容易地为分布式应用程序实现即插即用的基础管道。在大多数情况下,我们不需要重新编译或重新部署客户端/服务器端软件应可以完成,因为这些细节者交给了应用程序配置文件(和老的.net远程调用API很相似)。
WCF应用程序的基本构成
构建wcf分布式系统时一般会创建三个相互关联的程序集。
- WCF服务程序集:这个*.dll包含了表示希望外部用户公开的整体功能的类和接口。
- WCF服务宿主:这个软件模块是承载wcf服务程序集的实体。
- WCF客户端:这是通过中间代理访问服务功能的应用程序。
WCF的ABC
宿主和客户端相互通信会遵循ABC,再提配一下,注意构建WCF应用程序的核心模块,具体而言就是地址(A,address)、绑定(B,binding)和契约(C,contract)。
- 地址:服务的位置。在代码中,用System.Uri类型表示,然而,值一般保存在*.config文件中。
- 绑定:WCF附带了许多不同的绑定来指定网络协议、编码和传输层。
- 契约:从WCF服务公开的每个方法的描述。
缩略语ABC并不意味着开发人员必须首先定义地址,然后定义绑定,最后定义契约。在许多情况下,WCF开发人员都是首先为服务定义契约,然后定义地址和绑定。
WCF契约
契约的概念对于构建wcf服务来说是关键的。尽管不是强制性的,但是绝大部分wcf应用程序都会首先定义一组.net接口类型。这些类型用来表示某一给定的wcf类型将会支持的一组成员。确切地说,表示wcf契约的接口称为服务契约。实现了这些接口的类称作服务类型。
WCF绑定
定义并实现一个(或者一组)契约后,接下来就该为wcf服务构建承载代理了。你会看到有多种承载方式可以选择,所有这些承载都必须指明一些绑定,远程调用者通过使用这些绑定来获取对服务类型功能的访问。WCF绑定可包含如下特性:
- 由服务实现的契约。
- 用于移动数据的传输层(HTTP、MSMQ、命名管道、TCP)。
- 用于传输的信道(单向、请求响应、双向)。
- 用于处理数据本身的编码机制(XML、二进制)。
- 任何被支持的Web服务协议(如果绑定允许)如WS-Security、WS-Transaction等。
绑定举例:
- 基于HTTP的绑定(BasicHttpBinding、WSHttpBinding等)
- 基于TCP的绑定(NetNamedPipeBinding、NetPeerTcpBinding、NetTcpBinding)
- 基于MSMQ的绑定(MsmqIntegrationBinding、NetMsmqBinding)
WCF地址
WCF地址要吧指定如下一些信息
- 构架:传输协议(HTTP等)。
- 机器名:机器的完全限定域名。
- 端口:在很多情况下是可选的。
- 路径:WCF服务的路径
构架://<机器名>[:端口]/路径
基于Web服务的绑定:http://localhos:8080/MyWCFService
基于TCP的绑定:net.tcp://localhost:8080/MyWCFService
基于MMMQ的绑定:net.msmq://localhost/private$/MyPrivateQ
基于命名管道的绑定:net.pipe://localhost/MyWCFService
构建WCF服务
WCF服务接口标记了[ServiceContract]特性,而每一个接口成员都标记了[OperationContract]特性:
[ServiceContract] public interface IEightBall { //问一个问题,获取答案 [OperationContract] string ObtainAnswerToQuestion(string userQuestion); }
实现接口:
public class MagicEightBallService:IEightBall { //中是为了在主机上显示 public MagicEightBallService() { Console.WriteLine("The 8-Ball awaits your question..."); } public string ObtainAnswerToQuestion(string userQuestion) { string[] answers={"Future Uncertain","Yes","NO","Hazy","ask again later","definitely"}; //返回随机的响应 Random r=new Random(); Return string.Format("{0}?{1}.",userQuestion,answer[r.Next(answers.Length)]); } }
[ServiceContract]特性
为了使接口成WCF所提供的服务,我们必须用[ServiceContract]特性来修饰它。和许多其他的.net特性一样,ServiceContractAttribute类型支持许多用来限定其含义的属性。我们可以设置Name和Namespace这两个属性,用它们来控制服务类型的名称及其XML命名空间。当使用特定于Web服务的绑定时,这些值用来定义相关WSDL文档的<PortType>元素。
[OperationContract]特性
在wcf框架中使用的方法必须用[OperationContract]特性来修饰。
作为操作契约的服务类型
构建WCF服务类型并不需要使用接口。直接将[ServiceContract]特性和[OperationContract]特性应用到服务类型上也是可行的:
[ServiceContract(Namespace="http://Intertech.com")] public class ServiceTypeAsContract { [OperationContract] void SomeMethod(){} [OperationContract] void AnotherMethod(){} }
在App.config文件中创建ABC
在构建WCF服务的宿主时,你将会遵循一些常见的步骤(一部分是通过配置,一部分是通过代码),如下所示:
- 在宿主的配置文件中,定义承载的WCF服务的端点。
- 通过编程使用ServiceHost类型去提供端点所提供的服务类型。
- 确保宿主保持运行状态,以处理所收到的客户端请求。显然,如果你使用Windows服务或iis来承载服务类型,那么并不需要这一步
在WCF的世界中,术语“端点”只是表示地址、绑定和契约的一个包装。在xml中,端点使用<endpoint>元素和address、binding以及contract元素进行表达。更新我们的*.config文件来指定一个由主机公开的端:
<?xml version="1.0" enconding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="MagicEightBallServiceLib.MagicEightBallService"> <endpoint address="http://localhost:8080/MagicEightBallService" binding="basicHttpBinding" contract="MagicEightBallServiceLib.IEightBall" /> </service> </services> </system.serviceModel> </configuration>
针对ServiceHost类型进行编程
有了如上的配置文件,完成宿主的编程逻辑就很简单了。在启动可执行程序时,我们会创建ServiceHost类型的实例。在运行时,这个对象会自动读取宿主*.config文件中<system.serviceModel>元素中的数据来检测正确的地址、绑定类型和契约并且创建必要的管道:
static void Main(string[] args) { Console.WriteLine("Console based WCF Host"); using(ServiceHost serviceHost=new ServiceHost(typeof(MagicEightBallService))) { //打开宿主并且开启对传入消息的监听 serviceHost.Open(); //使服务保持运行状态,直到回车键被按下 Console.WriteLine("The service is ready."); Console.WriteLine("Press the Enter key to terminate service"); Console.ReadLine(); } }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步