WCF是什么东东?
WCF,是.NET Framework 3.0中的四个组件之一,是微软专门针对面向服务(Service Oriented)应用程序提供的一个分布式编程框架,可以使用托管代码建立和运行SOA的软件系统。它使得开发者能够建立一个跨平台的、安全、可信赖、事务性的解决方案,且能与已有系统兼容协作。WCF是微软分布式应用程序开发的集大成者,它整合了.Net平台下所有的和分布式系统有关的技术,例如.Net Remoting、ASMX、WSE和MSMQ,并且从分整合了.Net Remoting/Asp.Net/Xml/Web Service/MSMQ/WSE/Enterprise Service等多项分布式技术,取其精华,弃其糟粑 。以通信(Communiation)范围而论,它可以跨进程、跨机器、跨子网、企业网乃至于Internet;以宿主程序而论,可以以ASP.NET,EXE,WPF,Windows Forms,NT Service,COM+作为宿主(Host)。WCF可以支持的协议包括TCP,HTTP,跨进程以及自定义,安全模式则包括SAML,Kerberos,X509,用户/密码,自定义等多种标准与模式。全称"Windows Communication Foundation".
整个WCF的架构结构图下图所示(图片来源http://dev.yesky.com/402/8079902.shtml)
WCF整个架构层次简单的说明:
Contracts(契约):契约定义了整个消息系统的各个方面。而WCF契约有分好多种,数据契约描述了服务传递的每个消息的具体参数;消息契约使用SOAP来定义消息的具体格式;服务契约定义服务接口的方法签名;而策略和绑定规定访问服务的通信条件。(这些具体的契约我们后续一起慢慢完善它,先了解了解哈!大牛Artech等博客上都有详细的描述哈)。
Service Runtime(服务运行时):服务运行时包含了在对服务进行实际操作时才发生的一些行为,即是服务的运行时行为。WCF也分好几种,节流阀行为(Throttling Behavior),控制着有多少消息能被处理;错误行为(Error Behavior)设定服务出现内部错误时,控制哪些信息被传递到客户端;元数据行为(Metadata Behavior)控制着哪些元数据暴露给外部;实例行为(Instance Behavior)控制着能运行多少服务的实例;事务行为(Transaction Behavior)在出现错误时保证事务操作能回滚;调度行为(Dispatch Behavior)控制着消息如何被整个WCF基础结构进行处理;并发行为(Concurrency Behavior)控制在服务运行的并发处理;参数过滤器(Parameter Filtering)控制着参数的过滤条件。
Messaging(消息):消息层实际上由一些通道(Channel)所组成。所谓通道,就是一个以特定方式处理消息的组件。一系列的通道串联起来就成为通道栈。通道分为两种类型,协议通道和传输通道。协议通道有:WS安全协议通道(WS-Security Channel)、WS消息可靠性协议通道(WS-Reliability Channel)。传输通道有:HTTP通道(HTTP Channel)、TCP通道(TCP Channel)、命名管道通道(NamedPipe Channel)和消息队列通道(MSMQ Channel)。另外还有些编码通道(Encoders Channel)和事务流通道(Transaction Flow Channel)作为辅助,有兴趣的朋友可以更深的了解了解。
Hosting and Activation(宿主和激活):服务必须在一个执行程序中运行。服务一般托管在外部可执行程序里面,如IIS和Windows激活服务(Windows Activation Service,WAS)。
以上是对WCF架构层次的简要说明,是不是感觉这个东西很强大啊!嘿嘿,那就写一个用WCF的小实例吧!
做一个订车票简单的简单小例子吧!首先来检查一下自己机器的环境,看是否满足开发WCF的需求(嘿嘿,瞎扯现在估计我们猿人的编程机器不想过去那么落后吧)!
开始,我们先做一个WCF的应用程序,如下图1。
图1.
当点击完成后,VS会为我们创建好如下图2的项目。
图2.
从图2可以一目了然的看到IService1.cs应该就是传说中的WCF的服务契约,当然上图是VS自动生成的一个结构。开始接才接触的人肯定奇怪怎么还有一个Service1.svc的文件,这个文件是干什么的么?
那就扯开话题来看看这个是怎么回事—>在archive博客上有这样的描述:在采用IIS/WAS进行服务寄宿的情况下,我们需要为寄宿的服务创建一个.svc文件。在通常的情况下(当然你也可以以内联的形式将整个服务类型也定义其中),我们仅仅在该.svc文件中定义基本的<%@ServiceHost%>指令信息。其中最重要的指令信息自然是通过Service属性指定的寄宿服务的类型(实际上调用ServiceHostFactory的CreateServieHost方法传入的第一个参数值)。进一步来说,如果服务端能够维护一个Service/ServiceHostFactory与请求地址之间的映射关系,我们就可以不再需要.svc文件,因为.svc对于服务激活来说就是起到了这么一个映射的作用。
嘿嘿,恍然大悟吧!这个Service1.svc在这里根据VS生成的代码是实现IService1.cs里的服务契约。转回话题,我们直接来继续写代码吧!
下面就是直接修改IService1.cs的服务契约:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
namespace HuiTaiWcf { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together. [ServiceContract] public interface IService1 { [OperationContract] void Add_Ticket(int count); //添加车票的方法 [OperationContract] int Buy_Tickets(int Num); //购买车票的方法 [OperationContract] int GetdemandNum(); //查询车票的方法 // TODO: Add your service operations here } // Use a data contract as illustrated in the sample below to add composite types to service operations. [DataContract] public class Ticket { bool T_Count = true; //判断是否还有车票 int T_HowMany = 50; //默认的车票 [DataMember] public bool BoolTicket { get { return T_Count; } set { if (HowMany > 0) { T_Count = false; } else { T_Count = true; } } } [DataMember] public int HowMany { get { return T_HowMany; } set { T_HowMany = value; } } } }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
namespace HuiTaiWcf { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together. public class Service1 : IService1 { Ticket ticket = new Ticket(); //实例化一下我们自定义的Ticket类型 //实现一下契约的添加方法 public void Add_Ticket(int count) { this.ticket.HowMany = this.ticket.HowMany + count; } //实现一下契约的购买方法 public int Buy_Tickets(int Num) { if (this.ticket.BoolTicket) { this.ticket.HowMany = this.ticket.HowMany - Num; return 1; } else { return 0; } } //实现一下契约的查询方法 public int GetdemandNum() { return this.ticket.HowMany; } } }
到这里,我们的服务端就暂时说一下( ^_^ )/~~拜拜!搞好服务,是不是要一个漂亮的东东来启动一下我们费劲写的服务呢!
那就加winForm窗体进来玩玩吧!添加项目如下图3.
图3.
早上换了一个环境,但是不影响我们的步伐,继续写。
先画一个WinFrom窗体程序,大体模样如下图4.
图4.为了好看加了一个背景图。窗体画好,直接写后台吧!
哦,这里要注意一点,因为我们用WinFrom来写WCF服务端的一个开启服务的宿主程序,所以我们要在项目里应用System.ServiceModel这个DLL,为什么呢!因为WCF中的大部分类和接口都在这个DLL里面呢!
来看WinFrom后台代码,这里无非也就是2个按钮的事件罢了。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
namespace ServiceForms { public partial class ServiceForm : Form { public ServiceForm() { InitializeComponent(); } //定义ServiceHost ServiceHost host = null; //启动服务 private void butStart_Click(object sender, EventArgs e) { //HuiTaiWcf.Service1需要引用服务端的DLL host = new ServiceHost(typeof(HuiTaiWcf.Service1)); host.Open(); this.label.Text = "服务已经启动"; } //关闭服务 private void butstop_Click(object sender, EventArgs e) { //判断服务是否关闭 if(host.State != CommunicationState.Closed) { host.Close(); } this.label.Text = "服务已经关闭"; } } }
这里相信大家一看也是一目了然的代码了,要说的无非也就是这个"ServiceHost",那就简单的说下"ServiceHost",一旦创建好ServiceHost对象,通过它就可以创建WCF运行时(Runtime),WCF 运行时是自一组负责接受和发送消息的对象。这个就简单的了解下,后续我们在共同学习。
接下来为我们启动服务的的WinFrom程序来创建一个App.config文件,来配置一下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <!--添加服务--> <service name="HuiTaiWcf.Service1" behaviorConfiguration="CalculatorServiceBehavior"> <!--name必须与代码中Host实例初始化的服务一样--> <host> <baseAddresses> <!--添加调用服务的地址--> <add baseAddress="http://localhost:8000"/> </baseAddresses> </host> <!--添加契约接口contract="WcfDemo.IService1"WcfDemo.IService1为契约接口--> <endpoint address ="" binding="wsHttpBinding" contract="HuiTaiWcf.IService1" ></endpoint> </service> </services> <!--定义CalculatorServiceBehavior的行为--> <behaviors> <serviceBehaviors> <behavior name="CalculatorServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
这个配置算对运用WCF的契约那可是很重要的,这就是WCF中出名的ABC.有些人可能还不知道ABC是什么东东吧!那就撤下这个所谓的WCF中出名的ABC吧!
说ABC之前,先来说下Endpoints,它是WCF实现通信的核心要素,它可以是一个,也可以是一族;WCF Service由一个Endpoints集合组成,每个Endpoint就是用于通信的入口,客户端和服务端通过Endpoint交换信息。如下图5.
图5.
从图中就可以看出Endpoint是由三部分组成:Address,Binding,Contract.这个就是WCF中出名的ABC,有了他们客户端和服务端通过就能很好的实现通讯。那他们具体都是什么呢!那就在简单的说下。Address是Endpoint的网络地址,它标记了消息的发送的目的地。Binding描述是如何发送消息。Contract则描述的是消息所包含的内容,以及消息的组织和操作方式。可以这么理解:当WCF发送消息时,通过Address知道消息的发送的地址,通过Binding知道怎样来发送它,通过contract知道发送的消息是什么。
转会话题,配置弄好了,来看看我们的WinFrom是不是能启动服务端呢!
我们需要在我们的WinFrom程序右键,点击选择"调试"里面的"启动新的实例",如图6.
图6.
启动好以后,我们的Winfrom窗体就出来,我们点击"开启服务"看看,如下图7.
图7.到这里说明我已经启动了WCF的服务,为了更验证,我们只需要在浏览器里输入我们刚才在App.config里配置的添加调用的服务地址,我配置的是:http://localhost:8000/,我们来试着访问下,访问结果如下图8所以。
图8.到这里已经说明我们的服务端和启动的服务已经是ok的了,我们启动WCF的服务,无非就想要暴露里面的契约,使其他程序使用或者实现,那么我们是不是还差一个客户端程序,那就在写一个简单的客户端吧!客户端用控制台不太友好,那就干脆在来一个WinFrom程序吧!我们在我们的项目就在添加一个WinFrom程序吧!
在新建我们的客户端之前我在这里说一个小提示:所有系统是Win7的朋友们,当你在运行那个WInFrom开关程序时,可能会出现一个"HTTP无法注册 UrL Http://+8000/. 进程不具有该命名空间的权限"的异常,如下图9.
图9.解决这个异常的办法就是你在启动Visual Studio时,右键选择"以管理员身份运行"即可。
转回来搞我们的客户端程序吧!
添加我们的客户端WinFrom程序吧!如下图10.
图10.添加完之后就画个窗体吧!如图11.
图11.然后我们就要写后台程序了,现在我们是万事俱备只欠东风了。在写后台之前我们需要建立WCF引用了。
具体的操作如图12.我们在我们客户端WinFrom程序里的引用上右键,"添加服务引用",当我们点击添加服务引用后,会出现如图12.的窗体
图12.当出现这个窗口时,开始里面是空白的什么都没有,这个时候你可以选择"发现"按钮,VS则会自动在你的解决方案里检查暴露的服务。当然你明确服务的地址的话,可以在地址栏里面输入服务的URL。当我们找到服务后,就把它引用到我们的客户端的winFrom程序吧!
引入到项目,行了里会多了如下图13的东西。
图13.有兴趣的朋友可以去看看了解了解,Artech等人博客上都有这方面的知识。
现在就把我们客户端的后台给随便搞了呗!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
namespace WindowsWCF { public partial class WindowWCF : Form { public WindowWCF() { InitializeComponent(); } //声明在客户端调用服务 ServiceReference1.Service1Client T_Client = new ServiceReference1.Service1Client(); //购买 private void butGet_Click(object sender, EventArgs e) { int o = this.T_Client.Buy_Tickets(2); //调用WCF中的方法 if (o==1) { this.label1.Text = "购买成功"; } this.label1.Text = "还剩下车票" + this.T_Client.GetdemandNum().ToString() + "张"; } //查询 private void butmian_Click(object sender, EventArgs e) { this.label1.Text = ""; this.label1.Text = this.T_Client.GetdemandNum().ToString(); } } }
现在客户端的程序也完成了代码里注释很明确,那跑起来看看,看我们的WCF的服务到底客户端怎么样呢!如图14所示的结果.
图14.
呵呵!一个简简单单的例子希望对新学西WCF的朋友有点帮助,我也是才接触这东西,那里有什么错误的地方,还请大家多多指导,大家一起共同学习!