.Net中关于SOA的三大组件之WCF
上一篇介绍了WebService,想想也是刚毕业那时候进入公司就接触的WebService,所以也相对熟悉一些。而WCF本人在实际应用中也很少使用,但平常的时候也有所了解。
WCF是.NetFramework3.5推出的,被称之为分布式服务的集大成者。它不像WebService只能寄宿在IIS上,它支持不同的协议,比如http、tcp、 IPC、 MSMQ、 p2p,并且支持不同的宿主:网站、控制台、winform、WindowsService。同时也可以支持双工通信。
当我们在VS上创建一个WCF项目时,其为我们生成Service1.svc和IService1.cs接口。下面来看一下一个WCF方法需要哪些东西吧。
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。 [ServiceContract] public interface IService1 { [OperationContract] string GetData(int value); [OperationContract] CompositeType GetDataUsingDataContract(CompositeType composite); // TODO: 在此添加您的服务操作 } // 使用下面示例中说明的数据约定将复合类型添加到服务操作。 [DataContract] public class CompositeType { bool boolValue = true; string stringValue = "Hello "; [DataMember] public bool BoolValue { get { return boolValue; } set { boolValue = value; } } [DataMember] public string StringValue { get { return stringValue; } set { stringValue = value; } } }
上面代码是我们新建项目时为我们生成的,从中可以窥探一二。
- 首先WCF需要一个抽象接口,该接口需要添加特性:ServiceContract
- 接口里面的方法需要添加特性OperationContract才可以被调用
- 实体具有无参数构造函数时,可以不标记DataContract特性,调用时会将数据全部返回
- 实体没有无参数构造函数时,需要添加DataContract,当实体类上面有DataContract时,那么类中成员变量需要添加[DataMember],否则不可见
- 真实工作中,都会带上DataContract 和 DataMember
我们知道,将WCF应用部署到IIS上很简单,这里就不多说了。下面看一下将WCF通过TCP的方式托管在控制台或Winform或WindowsService上怎么做。其实也很简单,也是通过配置文件来配置,然后在程序中使用System.ServiceModel.ServiceHost来开启服务(注意权限问题)。
<?xml version="1.0" encoding="utf-8" ?>"v4.0" sku=".NETFramework,Version=v4.5" /> "MathServicebehavior"> "false"/> "false"/> "00:10:00"/> "1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> "CalculatorServicebehavior"> "false"/> "false"/> "00:10:00"/> "1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> "tcpbinding"> "None"> "None" protectionLevel="None"/> "SOA.WCF.Service.CalculatorService" behaviorConfiguration="CalculatorServicebehavior"> "net.tcp://localhost:11111/CalculatorService"/> "" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="SOA.WCF.Interface.ICalculatorService"/> "mex" binding="mexTcpBinding" contract="IMetadataExchange"/> "SOA.WCF.Service.MathService" behaviorConfiguration="MathServicebehavior"> "net.tcp://localhost:11111/MathService"/> "" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="SOA.WCF.Interface.IMathService"/> "mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ServiceModel; using SOA.WCF.Service; using SOA.WCF.Interface; using System.ServiceModel.Description; namespace SOA.Project { public class ServiceInit { public static void Process() { //ServiceHost对象 Listhosts = new List () { new ServiceHost(typeof(MathService)), new ServiceHost(typeof(CalculatorService)) }; foreach (var host in hosts) { host.Opening += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备打开"); host.Opened += (s, e) => Console.WriteLine($"{host.GetType().Name} 已经正常打开"); host.Closing += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备关闭"); host.Closed += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备关闭"); host.Open(); } Console.WriteLine("输入任何字符,就停止"); Console.Read(); foreach (var host in hosts) { host.Close(); } Console.Read(); #region MyRegion //using (ServiceHost host = new ServiceHost(typeof(MathService))) //{ // #region 程序配置 // host.AddServiceEndpoint(typeof(IMathService), new WSHttpBinding(), "http://127.0.0.1:9999/calculatorservice"); // if (host.Description.Behaviors.Find () == null) // { // ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); // behavior.HttpGetEnabled = true; // behavior.HttpGetUrl = new Uri("http://127.0.0.1:9999/calculatorservice/metadata"); // host.Description.Behaviors.Add(behavior); // } // #endregion 程序配置 // host.Opened += (s, e) => // { // Console.WriteLine("MathService已经启动,按任意键终止服务!"); // }; // host.Open(); // Console.Read(); //} #endregion } private static void Host_Closing(object sender, EventArgs e) { throw new NotImplementedException(); } } }
那为什么采用http方式部署到IIS那么简单,我们还要使用其他协议来部署呢?这个当然有各个协议的优点和具体的用处了,例如TCP协议速度快点,但是不能穿透防火墙,可是其是有状态的而http是无状态的。而且通常WCF会和WindowsService结合被用来做定时调度任务,关于任务调度可以看我这篇博文 : 。
因为WCF可以采用TCP、Pipeline协议,所以WCF还可以做双工通信,其核心思路就是回调,具体做法:
- 在服务接口上约定,CallbackContract(回调接口)
- 回调接口里面方法需要IsOneWay=true
- 启动服务,客户端引用服务,实现回调接口
- 调用服务时,讲回调方法传递进去
- 不仅是客户端可以向服务端发消息,而且服务端可以主动向客户端发消息
下面我们来看下具体的代码
[ServiceContract(CallbackContract = typeof(ICallBack))] public interface ICalculatorService { [OperationContract(IsOneWay = true)] void Plus(int x, int y); } ////// 不需要协议 是个约束,由客户端实现 /// public interface ICallBack { [OperationContract(IsOneWay = true)] void Show(int m, int n, int result); } public class CalculatorService : ICalculatorService { /// /// 完成计算,然后去回调 /// /// /// public void Plus(int x, int y) { int result = x + y; Thread.Sleep(2000); ICallBack callBack = OperationContext.Current.GetCallbackChannel (); callBack.Show(x, y, result); } }
最后关于WCF的安全,通常WCF一般是给后端用的,也是在内网中使用,但是也不排除有些公司提供给第三方的接口采用的是WCF服务,像我现在公司就是,所以也要注意到其安全性。那么关于WCF的一般可以有以下的方式,具体怎么做就不多说了。
- 没有身份验证--局域网后台调用
- 发布口令--请求式加个参数(事先约定)
- soapheader--用户的账号密码
- Windows 身份验证
- X509证书