测试代码链接
Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架,可以翻译为Windows 通讯开发平台。 整合了原有的windows通讯的 .net Remoting,WebService,Socket的机制,并融合有HTTP和FTP的相关技术。 是Windows平台上开发分布式应用最佳的实践方式。 简单的归结为四大部分 1>.网络服务的协议,即用什么网络协议开放客户端接入。 2>.业务服务的协议,即声明服务提供哪些业务。 3>.数据类型声明,即对客户端与服务器端通信的数据部分进行一致化。 4>.传输安全性相关的定义。 它是.NET框架的一部分,由 .NET Framework3.0 开始引入,与Windows Presentation Foundation及Windows Workflow Foundation并行为新一代 Windows操作系统以及 WinFX 的三个重大应用程序开发类库。在 .NET Framework2.0 以及前版本中,微软发展了 Web Service(SOAP with HTTP communication),.NET Remoting (TCP/HTTP/Pipeline communication) 以及基础的 Winsock 等通信支持。由于各个通信方法的设计方法不同,而且彼此之间也有相互的重叠性,对于开发人员来说,不同的选择会有不同的程序设计模型,而且必须要重新学习,让开发人员在使用中有许多不便。同时,面向服务架构(Service-Oriented Architecture) 也开始盛行于软件工业中,因此微软重新查看了这些通信方法,并设计了一个统一的程序开发模型,对于数据通信提供了最基本最有弹性的支持,这就是 Windows Communication Foundation。
WCF 由于集合了几乎由.NET Framework提供的所有的通信方法,因此学习曲线比较陡峭,开发人员必须要针对各个部份的内涵做深入的了解,才能够操控 WCF 来开发应用程序。 通信双方的沟通方式,由合约来订定。通信双方所遵循的通信方法,由协议绑定来订定。通信期间的安全性,由双方约定的安全性层次来订定。
契约 WCF 的基本概念是以契约(Contract) 来定义双方沟通的协议,合约必须要以接口的方式来体现,而实际的服务代码必须要由这些合约接口派生并实现。合约分成了四种:数据契约(Data Contract),订定双方沟通时的数据格式。服务契约(Service Contract),订定服务的定义。操作契约(Operation Contract),订定服务提供的方法。消息契约(Message Contract),订定在通信期间改写消息内容的规范。一个 WCF 中的契约,就如同下列代码所示:
using System; using System.ServiceModel; namespace Microsoft.ServiceModel.Samples { [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")] // 服务契约 public interface ICalculator { [OperationContract] // 操作契约 double Add(double n1, double n2); [OperationContract] //操作契约 double Subtract(double n1, double n2); [OperationContract] // 操作契约 double Multiply(double n1, double n2); [OperationContract] //操作契约 double Divide(double n1, double n2); } }协议绑定 由于 WCF 支持了HTTP,TCP,Named Pipe,MSMQ,Peer-To-Peer TCP 等协议,而 HTTP 又分为基本 HTTP 支持 (BasicHttpBinding) 以及 WS-HTTP 支持 (WsHttpBinding),而 TCP 亦支持 NetTcpBinding,NetPeerTcpBinding 等通信方式,因此,双方必须要统一通信的协议,并且也要在编码以及格式上要有所一致。一个设置通信协议绑定的示例如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <!-- 设定服务系统的资讯 --> <services> <service name=" CalculatorService" > <endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="ICalculator" /> </service> </services> <!-- 设定通讯协定系统的资讯 --> <bindings> <wsHttpBinding> <binding name="Binding1"> </binding> </wsHttpBinding> </bindings> </system.serviceModel> </configuration>虽然 WCF 也可以使用SOAP做通信格式,但它和以往的ASP. NETXML Web Services不同,因此有部分技术文章中,会将 ASP. NET 的 XML Web Services 称为ASMX Service。WCF 的服务可以挂载于 Console Application,Windows Application,IIS (ASP. NET) Application,Windows Service以及Windows Activation Services中,但大多都会挂在 Windows Service。
安全性层次 WCF 实现上已经支持了传输层次安全性 (Transport-level security) 以及消息层次安全性 (Message-level security) 两种。传输层次安全性:在数据传输时期加密,例如 SSL。消息层次安全性:在数据处理时就加密,例如使用数字签名,散列或是使用密钥加密法等。
对于 WCF 的客户端来说,WCF 服务就像是一个 Web Service 一样,在 Visual Studio 2008 中,所有 WCF 服务的连接都是由客户端的 服务代理(WCF Service Proxy) 来运行,开发人员不用花费太多心思在通信上,而 WCF Service Proxy 在 Visual Studio 中被称为服务引用(Service Reference)。在 Visual Studio 中加入 WCF 的服务引用时,Visual Studio 会自动帮开发人员做掉一些必要工作(例如组态创建以及产生 Service Proxy 等),开发人员只需要在代码中取用 WCF Service Proxy 对象即可。
在David Chappell所撰的《Introducing Windows Communication Foundation》一文中,用了一个活鲜鲜的例子,来说明WCF的优势所在。假定我们要为一家汽车租赁公司开发一个新的应用程序,用于租车预约服务。该租车预约服务会被多种应用程序访问,包括呼叫中心(Call Center),基于J2EE的租车预约服务以及合作伙伴的应用程序(Partner Application)。从功能的角度来看,WCF完全可以看作是ASMX,.Net Remoting,Enterprise Service,WSE,MSMQ等技术的并集。(注:这种说法仅仅是从功能的角度。事实上WCF远非简单的并集这样简单,它是真正面向服务的产品,它已经改变了通常的开发模式。)因此,对于上述汽车预约服务系统的例子,利用WCF,就可以解决包括安全、可信赖、互操作、跨平台通信等等需求。开发者再不用去分别了解.Net Remoting,ASMX等各种技术了。概括地说,WCF具有如下的优势: 1、统一性 前面已经叙述,WCF是对于ASMX,.Net Remoting,Enterprise Service,WSE,MSMQ等技术的整合。由于WCF完全是由托管代码编写,因此开发WCF的应用程序与开发其它的.Net应用程序没 有太大的区别,我们仍然可以像创建面向对象的应用程序那样,利用WCF来创建面向服务的应用程序。 2、互操作性 由于WCF最基本的通信机制是SOAP(Simple Object Access Protocol 简易对象访问协议),这就保证了系统之间的互操作性,即使是运行不同的上下文中。这种通信可以是基于.Net到.Net间的通信,如下图所示: 可以跨进程、跨机器甚至于跨平台的通信,只要支持标准的Web Service,例如J2EE应用服务器(如WebSphere,WebLogic)。应用程序可以运行在Windows操作系统下,也可以运行在其他的操作系统,如Sun Solaris,HP Unix,Linux等等。如下图所示: 3、安全与可信赖 WS-Security,WS-Trust和WS-SecureConversation均被添加到SOAP消息中,以用于用户认证,数据完整性验证,数据隐私等多种安全因素。 在SOAP 的header中增加了WS-ReliableMessaging允许可信赖的端对端通信。而建立在WS-Coordination和WS- AtomicTransaction之上的基于SOAP格式交换的信息,则支持两阶段的事务提交(two-phase commit transactions)。上述的多种WS-Policy在WCF中都给与了支持。对于Messaging而言,SOAP是Web Service的基本协议,它包含了消息头(header)和消息体(body)。在消息头中,定义了WS-Addressing用于定位SOAP消息的地址信息,同时还包含了MTOM(消息传输优化机制,Message Transmission Optimization Mechanism)。 4、兼容性 WCF充分的考虑到了与旧有系统的兼容性。安装WCF并不会影响原有的技术如ASMX和.Net Remoting。即使对于WCF和ASMX而言,虽然两者都使用了SOAP,但基于WCF开发的应用程序,仍然可以直接与ASMX进行交互。
WCF 的服务挂载于IIS (ASP. NET) Application
Visual Studio 2019 找不到WCF服务解决办法 没有看到WCF服务模板,则可能需要安装Visual Studio 的Windows Communication Foundation组件。选择“ 安装更多工具和功能”以打开Visual Studio Installer。选择单个组件选项卡,向下滚动到开发活动,然后选择Windows Communication Foundation。单击修改。
在web项目中添加wcf会自动生成两个文件后手动添加代码(wcf接口和派生类) WCF 的基本概念是以契约(Contract) 来定义双方沟通的协议,合约必须要以接口的方式来体现,而实际的服务代码必须要由这些合约接口派生并实现 其中wcf服务端(宿主项目)配置文件对于wcf的配置都是IDE自动生成
在浏览器中查看
在测试应用程序中添加WCF服务引用 客户端测试应用程序配置文件对于wcf的配置都是IDE自动生成
调用wcf服务 输出结果
以控制台应用程序做wcf宿主,控制台应用程序配置文件添加wcf配置信息
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /> </startup> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="ContactServicebehavior"> <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior> </serviceBehaviors> </behaviors> <bindings> <netTcpBinding> <binding name="tcpbinding"> <security mode="None"> <transport clientCredentialType="None" protectionLevel="None"/> </security> </binding> </netTcpBinding> </bindings> <services> <service name="WCF.Service.ContactService" behaviorConfiguration="ContactServicebehavior"> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:9002/ContactService"/> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="WCF.Interface.IContactService"/> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/> </service> </services> <!--<behaviors> <serviceBehaviors> <behavior name="ContactServicebehavior"> <serviceDebug httpHelpPageEnabled="false"/> <serviceMetadata httpGetEnabled="false"/> <serviceTimeouts transactionTimeout="00:10:00"/> <serviceThrottling maxConcurrentCalls="1000" maxConcurrentInstances="1000" maxConcurrentSessions="1000"/> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="httpbinding"/> </basicHttpBinding> </bindings> <services> <service name="WCF.Service.ContactService" behaviorConfiguration="ContactServicebehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:9001/ContactService"/> </baseAddresses> </host> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpbinding" contract="WCF.Interface.IContactService"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services>--> </system.serviceModel> </configuration>关于WCF中的地址和绑定补充: WCF中支持的传输协议包括HTTP、TCP、Peer network(对等网)、IPC(基于命名管道的内部进程通信)以及MSMQ(微软消息队列)。 每个协议对应一个地址类型: • HTTP地址:http://localhost:8080/ • TCP地址: net.tcp://localhost:8080/ • IPC地址: net.pipe://localhost/ (适用于跨进程,不能使用于不同机器间) • MSMQ地址: net.msmq://localhost/ • 对等网地址: net.p2p://localhost/
WCF中提供的绑定有: • BasicHttpBinding: 最简单的绑定类型,通常用于 Web Services。使用 HTTP 协议,Text/XML 编码方式。 • WSHttpBinding: 比 BasicHttpBinding 更加安全,通常用于 non-duplex 服务通讯。 • WSDualHttpBinding: 和 WSHttpBinding 相比,它支持 duplex 类型的服务。 • WSFederationHttpBinding: 支持 WS-Federation 安全通讯协议。 • NetTcpBinding: 效率最高,安全的跨机器通讯方式。 • NetNamedPipeBinding: 安全、可靠、高效的单机服务通讯方式。 • NetMsmqBinding: 使用消息队列在不同机器间进行通讯。 • NetPeerTcpBinding: 使用 P2P 协议在多机器间通讯。 • MsmqIntegrationBinding: 使用现有的消息队列系统进行跨机器通讯。如 MSMQ。
使用MVC应用程序添加服务引用,用服务基地址net.tcp://localhost:9002/ContactService来添加服务引用,配置文件将自动添加配置信息(添加服务引用前先以管理员权限运行wcf宿主控制台应用程序,将服务挂起) MVC控制器调用WCF服务
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using WCFModel; namespace SOAWebApp.Controllers { public class WcfController : Controller { // GET: Wcf public ActionResult WcfService1() { //using (WcfService1Reference.WcfService1Client client =new WcfService1Reference.WcfService1Client()) //{ // client.DoWork(); // ViewBag.GetString = client.GetString(); // ViewBag.GetUserList = client.GetUserList(0); //} WcfService1Reference.WcfService1Client client = null; try { client = new WcfService1Reference.WcfService1Client(); client.DoWork(); ViewBag.GetString = client.GetString(); ViewBag.GetUserList = client.GetUserList(0); client.Close(); return View(); } catch (Exception) { if (client != null) client.Abort(); throw; } } public ActionResult CRUD() { return View(); } public ActionResult Select() { using (WcfService2Reference.ContactServiceClient client = new WcfService2Reference.ContactServiceClient()) { var a = client.Select(null); return Json(a, JsonRequestBehavior.AllowGet); } } public ActionResult Insert(Contact contact) { Contact contact1 = new Contact() { Name = contact.Name, PhoneNo = contact.PhoneNo, EmailAddress = contact.EmailAddress, Address = contact.Address }; using (WcfService2Reference.ContactServiceClient client = new WcfService2Reference.ContactServiceClient()) { client.Insert(contact1); } return Content("ok"); } public ActionResult Delete() { string strId = Request["strId"]; using (WcfService2Reference.ContactServiceClient client = new WcfService2Reference.ContactServiceClient()) { client.Delete(strId); } return Content("ok"); } public ActionResult ShowEdit() { string id = Request["rowsid"]; using (WcfService2Reference.ContactServiceClient client = new WcfService2Reference.ContactServiceClient()) { var a = client.Select(id); return Json(a, JsonRequestBehavior.AllowGet); } } public ActionResult Edit(Contact contact) { Contact contact1 = new Contact() { Name = contact.Name, PhoneNo = contact.PhoneNo, EmailAddress = contact.EmailAddress, Address = contact.Address }; using (WcfService2Reference.ContactServiceClient client = new WcfService2Reference.ContactServiceClient()) { client.Update(contact1); } return Content("ok"); } } }MVC视图返回wcf结果
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <link href="~/Content/themes/default/easyui.css" rel="stylesheet" /> <link href="~/Content/themes/icon.css" rel="stylesheet" /> <script src="~/Scripts/jquery-1.7.1.min.js"></script> <script src="~/Scripts/jquery.easyui.min.js"></script> <script src="~/Scripts/easyui-lang-zh_CN.js"></script> <script src="~/Scripts/datapattern.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script> <script src="~/Scripts/jquery.validate.min.js"></script> <script type="text/javascript"> $(function () { $("#addDiv").css("display", "none"); $("#editDiv").css("display", "none"); loadData(); }); function loadData(pars) { $('#tt').datagrid({ url: '/Wcf2CRUD/Select', title: '用户数据表格', width: 700, height: 400, fitColumns: true, //列自适应 nowrap: false, idField: 'Id',//主键列的列明 loadMsg: '正在加载用户的信息...', pagination: false,//是否有分页 singleSelect: false,//是否单行选择 pageSize: 10,//页大小,一页多少条数据 pageNumber: 1,//当前页,默认的 pageList: [5, 10, 15], queryParams: pars,//往后台传递参数 columns: [[//c.UserName, c.UserPass, c.Email, c.RegTime { field: 'ck', checkbox: true, align: 'left', width: 50 }, { field: 'Id', title: '编号', width: 60 }, { field: 'Name', title: '姓名', width: 80 }, { field: 'PhoneNo', title: '电话', width: 80 }, { field: 'EmailAddress', title: '邮箱', width: 120 }, { field: 'Address', title: '地址', width: 120 } ]], toolbar: [{ id: 'btnDelete', text: '删除', iconCls: 'icon-remove', handler: function () { deleteInfo(); } }, { id: 'btnAdd', text: '添加', iconCls: 'icon-add', handler: function () { addInfo(); } }, { id: 'btnEidt', text: '编辑', iconCls: 'icon-edit', handler: function () { showEditInfo(); } }], }); } //删除数据 function deleteInfo() { var rows = $('#tt').datagrid('getSelections');//获取所选择的行 if (!rows || rows.length == 0) { //alert("请选择要修改的商品!"); $.messager.alert("提醒", "请选择要删除的记录!", "error"); return; } $.messager.confirm("提示", "确定要删除数据吗", function (r) { if (r) { //获取要删除的记录的ID值。 var rowsLength = rows.length; var strId = ""; for (var i = 0; i < rowsLength; i++) { strId = strId + rows[i].Id + ",";//1,2,3, } //去掉最后一个逗号. strId = strId.substr(0, strId.length - 1); //将获取的要删除的记录的ID值发送到服务端. $.post("/Wcf2CRUD/Delete", { "strId": strId }, function (data) { if (data == "ok") { $('#tt').datagrid('reload');//加载表格不会跳到第一页。 //清除上次操作的历史的记录。 $('#tt').datagrid('clearSelections') } else { $.messager.alert("提醒", "删除记录失败!", "error"); } }); } }); } //添加数据 function addInfo() { $("#addDiv").css("display", "block"); $('#addDiv').dialog({ title: '添加用户数据', width: 300, height: 200, collapsible: true, maximizable: true, resizable: true, modal: true, buttons: [{ text: 'Ok', iconCls: 'icon-ok', handler: function () { $("#addForm").submit();//提交表单 } }, { text: 'Cancel', handler: function () { $('#addDiv').dialog('close'); } }] }); } //完成添加后调用该方法 function afterAdd(data) { if (data == "ok") { $('#addDiv').dialog('close'); $('#tt').datagrid('reload');//加载表格不会跳到第一页。 $("#addForm input").val("");//清空表单input标签数据 } } //展示一下要修改的数据. function showEditInfo() { //判断一下用户是否选择了要修改的数据 var rows = $('#tt').datagrid('getSelections');//获取所选择的行,也只能一行 if (rows.length != 1) { $.messager.alert("提示", "请选择要修改的数据", "error"); return; } //将要修改的数据查询出来,显示到文本框中。 var rowsid = rows[0].Id;//从选中的行拿id //$.post异步请求 $.post("/Wcf2CRUD/ShowEdit", { "rowsid": rowsid }, function (data) { $("#txtId").val(data.Id); $("#txtName").val(data.Name); $("#txtPhoneNo").val(data.PhoneNo); $("#txtEmailAddress").val(data.EmailAddress); $("#txtAddress").val(data.Address); }); $("#editDiv").css("display", "block"); $('#editDiv').dialog({ title: '编辑用户数据', width: 300, height: 200, collapsible: true, maximizable: true, resizable: true, modal: true, buttons: [{ text: 'Ok', iconCls: 'icon-ok', handler: function () { $("#editForm").submit();//提交表单 } }, { text: 'Cancel', handler: function () { $('#editDiv').dialog('close'); } }] }); } //更新以后调用该方法. function afterEdit(data) { if (data == "ok") { $('#editDiv').dialog('close'); $('#tt').datagrid('reload');//加载表格不会跳到第一页。 } else { $.messager.alert("提示", "修改的数据失败", "error"); } } </script> </head> <body> <div> <table id="tt" style="width: 700px;" title="标题,可以使用代码进行初始化,也可以使用这种属性的方式" iconcls="icon-edit"> </table> </div> <!--------------添加数据----------------------> <div id="addDiv"> @using (Ajax.BeginForm("Insert", "Wcf2CRUD", new { }, new AjaxOptions() { HttpMethod = "post", OnSuccess = "afterAdd" }, new { id = "addForm" })) { <table> <tr><td>姓名</td><td><input type="text" name="Name" /></td></tr> <tr><td>电话</td><td><input type="password" name="PhoneNo" /></td></tr> <tr><td>邮箱</td><td><input type="text" name="EmailAddress" /></td></tr> <tr><td>地址</td><td><input type="text" name="Address" /></td></tr> </table> } </div> <!--------------修改数据----------------------> <div id="editDiv"> @using (Ajax.BeginForm("Edit", "Wcf2CRUD", new { }, new AjaxOptions() { HttpMethod = "post", OnSuccess = "afterEdit" }, new { id = "editForm" })) { <input type="hidden" name="Id" id="txtId" /> <table> <tr><td>姓名</td><td><input type="text" name="Name" id="txtName" /></td></tr> <tr><td>电话</td><td><input type="text" name="PhoneNo" id="txtPhoneNo" /></td></tr> <tr><td>邮箱</td><td><input type="text" name="EmailAddress" id="txtEmailAddress" /></td></tr> <tr><td>地址</td><td><input type="text" name="Address" id="txtAddress" /></td></tr> </table> } </div> </body> </html>1 无身份验证 2 windows身份验证 3 用户账号密码 4 证书 5 口令(token)
可以参考 http://www.cnblogs.com/zuowj/p/5021270.html http://www.cnblogs.com/zuowj/p/5078500.html
概述 双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息。基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合。双工MEP又具有一些变体,比如典型的订阅-发布模式就可以看成是双工模式的一种表现形式。双工消息交换模式使服务端回调(Callback)客户端操作成为可能。 在Wcf中不是所有的绑定协议都支持回调操作,BasicHttpBinding,WSHttpBinding绑定协议不支持回调操作;NetTcpBinding和NetNamedPipeBinding绑定支持回调操作;WSDualHttpBinding绑定是通过设置两个HTTP信道来支持双向通信,所以它也支持回调操作。
两种典型的双工MEP 1.请求过程中的回调 这是一种比较典型的双工消息交换模式的表现形式,客户端在进行服务调用的时候,附加上一个回调对象;服务在对处理该处理中,通过客户端附加的回调对象(实际上是调用回调服务的代理对象)回调客户端的操作(该操作在客户端执行)。整个消息交换的过程实际上由两个基本的消息交换构成,其一是客户端正常的服务请求,其二则是服务端对客户端的回调。两者可以采用请求-回复模式,也可以采用单向(One-way)的MEP进行消息交换。下图描述了这样的过程,服务调用和回调都采用请求-回复MEP。 2.订阅-发布 订阅-发布模式是双工模式的一个典型的变体。在这个模式下,消息交换的双方变成了订阅者和发布者,若干订阅者就某个主题向发布者申请订阅,发布者将所有的订阅者保存在一个订阅者列表中,在某个时刻将主题发送给该主题的所有订阅者。实际上基于订阅-发布模式的消息交换也可以看成是两个基本模式下消息交换的组合,申请订阅是一个单向模式的消息交换(如果订阅者行为得到订阅的回馈,该消息交换也可以采用请求-回复模式);而主题发布也是一个基于单向模式的消息交换过程。订阅-发布消息交换模式如下图所示。 示例 创建一个简单的Wcf通信服务,包括使使用NetTcpBinding实现双工通信。
服务端wcf服务接口和回调接口 服务端wcf服务+回调绑定 服务端wcf服务配置 客户端添加服务引用,同上上上。。。
客户端对服务端回调函数的实现 客户端调用服务,服务端将会通过回调执行客户端回调函数,实现简单双工通讯