本篇先通过Web Service和Remoting Service创建服务,抛砖引玉,再体验WCF服务。首先一些基本面:
什么是WCF?
Windows Communication Foundation,微软的平台,用来搭建分布式的、互操作的应用程序。
什么是分布式应用程序?
如果把计算机看成是节点,分布式应用程序跑在2个或2个以上的节点上。比如,一个应用程序运行在一台电脑上,另一个服务运行在另外一台电脑上,应用程序消费所提供的服务。
为什么需要分布式应用程序?
比如有一个应用程序的结构是数据层,业务层,表现层,而这些层可能分布在不同的电脑上。当用户请求比较少的时候,可以把这些层放在同一台服务器上,当用户请求达到一定的数量级,为了不影响性能,这时后就需要把这些层扩展到不同的服务器上。
另外,一个公司可能消费另一个公司提供的服务,这也是分布式的。
什么是互操作应用程序?
一个应用程序如果可以和任何平台上的应用程序通讯,这个应用程序就是互操纵应用程序。Web Service是互操作应用程序。.NET remoting Service不是,它只能被.NET 应用程序消费。
为什么要学WCF?
比如有2个客户端应用程序。
比如一个是Java应用程序,接收HTTP协议,需要XML格式的返回信息。为此,我们可以创建一个Web Service服务。
比如另一个是.NET应用程序,接收TCP协议,需要二进制格式的返回信息,为此,我们可以创建一个Remoting Service。
在这里,作为开发者,需要同时学会使用Web Service和Remoting Service。
而WCF统一了这些方面,只要学会WCF,就能处理上诉的应用场景。WCF提供2个end point,在end point的configuraiton中设置协议以及信息格式。
创建Web Service
大致思路是:
→ 创建Web Service
→ 客户端引用服务,并调用服务方法
创建一个空的ASP.NET Web应用程序。
添加"HelloWebService.asmx"文件。
修改如下:
[WebService(Namespace = "http://tempuri.org/")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)][System.ComponentModel.ToolboxItem(false)]// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。// [System.Web.Script.Services.ScriptService]public class HelloWebService : System.Web.Services.WebService{[WebMethod]public string GetMessage(string name){return "Hello " + name;}}
在浏览器中浏览"HelloWebService.asmx"文件:
点击"GetMessage"方法,在界面中输入实参,点击"调用"。
显示如下:
"http://localhost:3087/HelloWebService.asmx/GetMessage"这个就是获取Web Service的具体地址。
在当前的解决方案下再添加一个空的ASP.NET Web应用程序,名称为"HelloWebClient",并创建一个名称为"WebForm1.aspx"的Web窗体。
右键"HelloWebClient"下的"引用",点击"添加服务引用"。
点击"确定",在"HelloWebClient"下多了引用的Web Service。
完善"WebForm1.aspx"页面。
<table><tr><td><asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><asp:Button ID="Button1" runat="server" Text="获取信息" OnClick="Button1_Click" /></td></tr><tr><td><asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></td></tr></table>
当在TextBox中输入信息,点击"获取信息按钮",调用Web Service服务,就可获取到信息。
protected void Button1_Click(object sender, EventArgs e){HelloWebService.HelloWebServiceSoapClient client = new HelloWebService.HelloWebServiceSoapClient();Label1.Text = client.GetMessage(TextBox1.Text);}
创建Remoting Service
大致思路是:
→ 写一个接口
→ 实现接口,并派生于MarshalByRefObject
→ 宿主,注册信道,规定端口
→ 应用程序,也注册信道,调用方法
打开一个新的Visual Studio界面。
创建一个名称为"HelloRemotingService"的类库。
在该类库下创建一个"IHelloRemotingService"的接口。
namespace HelloRemotingService{public interface IHelloRemotingService{string GetMessage(string name);}}
在"HelloRemotingService"类库所在解决方案下创建名称为"MyRemotingService"的类库。
在"MyRemotingService"的类库中,添加对"HelloRemotingService"类库的引用。
在"MyRemotingService"中添加名称为"Hello"的类文件。
using HelloRemotingService;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace MyRemotingService{public class Hello : MarshalByRefObject, IHelloRemotingService{public string GetMessage(string name){return "Hello" + name;}}}
现在需要宿主。在当前解决方案下添加一个名称为"RemotingServiceHost"的控制台应用程序。并添加对"HelloRemotingService"类库和MyRemotingService"类库的引用,再添加"System.Runtime.Remoting"组件。
......using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;using MyRemotingService;namespace RemotingServiceHost{class Program{static void Main(string[] args){Hello h = new Hello();//信道TcpChannel channel = new TcpChannel(9090);//向信道服务注册信道ChannelServices.RegisterChannel(channel,true);//注册服务端对象RemotingConfiguration.RegisterWellKnownServiceType(typeof(Hello), "GetMessage", WellKnownObjectMode.Singleton);Console.WriteLine("Remoting service启动了 @" + DateTime.Now);Console.ReadKey();}}}
在解决方案下创建一个名称为"HelloRemotingServiceClient"的窗体。创建如下界面:
添加对"HelloRemotingService"类库和MyRemotingService"类库的引用,再添加"System.Runtime.Remoting"组件。
......using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;using MyRemotingService;namespace HelloRemotingServiceClient{public partial class Form1 : Form{IHelloRemotingService client;public Form1(){InitializeComponent();TcpChannel channel = new TcpChannel();ChannelServices.RegisterChannel(channel, true);client = (IHelloRemotingService)Activator.GetObject(typeof(IHelloRemotingService), "tcp://localhost:9090/GetMessage");}private void button1_Click(object sender, EventArgs e){label1.Text = client.GetMessage(textBox1.Text);}}}
生成解决方案。
运行宿主程序。
运行窗体程序。
WCF登场
在Visual Studio中创建一个名称为"HelloWcf"的类库。
在"HelloWcf"的类库添加一个名称为"FirstWcf"的"WCF 服务",项目自动为我们创建了"IFristWcf"和"FirstWcf"两个类文件,并自动添加了对"System.ServiceModel"的引用。
修改"IFirstWcf"接口:
namespace HelloWcf{// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IFirstWcf”。[ServiceContract]public interface IFirstWcf{[OperationContract]string GetMessage(string name);}}
修改"FirstWcf"类:
namespace HelloWcf{// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的类名“FirstWcf”。public class FirstWcf : IFirstWcf{public string GetMessage(string name){return "Hello " + name;}}}
现在需要宿主。在当前解决方案下添加一个"WcfHost"的控制台应用程序。
为"WcfHost"添加对"System.ServiceModel"的引用。
为"WcfHost"添加对"HelloWcf"类库的引用。
再为"WcfHost"配置end point, 需要添加2个end point,一个用来接收HTTP协议,一个用来接收TCP协议。
在App.config中配置如下:
<?xml version="1.0" encoding="utf-8" ?><configuration><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup><system.serviceModel><services><service name="HelloWcf.FirstWcf" behaviorConfiguration="mexBehaviour"><endpoint address="HelloWcf" binding="basicHttpBinding" contract="HelloWcf.IFirstWcf"></endpoint><endpoint address="HelloWcf" binding="netTcpBinding" contract="HelloWcf.IFirstWcf"></endpoint><endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint><host><baseAddresses><add baseAddress="http://localhost:9090"/><add baseAddress="net.tcp://localhost:6060"/></baseAddresses></host></service></services><behaviors><serviceBehaviors><behavior name="mexBehaviour"><serviceMetadata httpGetEnabled="true"/></behavior></serviceBehaviors></behaviors></system.serviceModel></configuration>
把"WcfHost"设置为启动程序。
再启动一个新的Visual Studio。
■ Web客户端消费WCF服务
创建一个名称为"WcfClient"的空的ASP.NET网站。
打开宿主应用程序。
在浏览器中输入:http://localhost:9090
点击"http://localhost:9090/?wsdl"链接。
以上的的xml格式和Web Service是很像的。
回到名称为"WcfClient"的空的ASP.NET网站,右键"引用",点击"添加服务引用",填写如下:
点击"确定"。
这时,自动添加了对"System.ServiceModel"的引用,自动添加了"Service References"文件。
同时,自动在Web.config中增加了<system.serviceModel>节点。
<configuration><system.web><compilation debug="true" targetFramework="4.5" /><httpRuntime targetFramework="4.5" /></system.web><system.serviceModel><bindings><basicHttpBinding><binding name="BasicHttpBinding_IFirstWcf" /></basicHttpBinding><netTcpBinding><binding name="NetTcpBinding_IFirstWcf" /></netTcpBinding></bindings><client><endpoint address="http://localhost:9090/HelloWcf" binding="basicHttpBinding"bindingConfiguration="BasicHttpBinding_IFirstWcf" contract="HelloService.IFirstWcf"name="BasicHttpBinding_IFirstWcf" /><endpoint address="net.tcp://localhost:6060/HelloWcf" binding="netTcpBinding"bindingConfiguration="NetTcpBinding_IFirstWcf" contract="HelloService.IFirstWcf"name="NetTcpBinding_IFirstWcf"><identity><userPrincipalName value="PC201312021114Administrator" /></identity></endpoint></client></system.serviceModel></configuration>
添加一个aspx文件,编写如下:
<div><asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><asp:Button ID="Button1" runat="server" Text="获取信息" OnClick="Button1_Click" /><br /><asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></div>
编写按钮事件如下:
protected void Button1_Click(object sender, EventArgs e){HelloService.FirstWcfClient client = new HelloService.FirstWcfClient();Label1.Text = client.GetMessage(TextBox1.Text);}
浏览该页面,点击按钮,发生如下报错:
提示需要明确end point。
我们需要用到Web.config中的如下节点:
<endpoint address="http://localhost:9090/HelloWcf" binding="basicHttpBinding"bindingConfiguration="BasicHttpBinding_IFirstWcf" contract="HelloService.IFirstWcf"name="BasicHttpBinding_IFirstWcf" />
修改按钮事件如下:
protected void Button1_Click(object sender, EventArgs e){HelloService.FirstWcfClient client = new HelloService.FirstWcfClient("BasicHttpBinding_IFirstWcf");Label1.Text = client.GetMessage(TextBox1.Text);}
再次浏览页面,输入内容,点击按钮,一切正常。
■ 窗体应用程序消费WCF服务
重新打开一个Visual Studio, 创建一个名称为"WcfFormClient"的窗体应用程序。
添加服务引用如下:
设计界面如下:
编写按钮事件如下:
private void button1_Click(object sender, EventArgs e){HelloServiceTwo.FirstWcfClient client = new HelloServiceTwo.FirstWcfClient("NetTcpBinding_IFirstWcf");label1.Text = client.GetMessage(textBox1.Text);}
运行窗体应用程序,输入内容,点击按钮,一切正常。
至此,通过WCF的2个end point,客户端既可以使用HTTP协议通讯,也可以使用TCP协议通讯。
总结一下WCF服务的调用过程:
→ 创建WCF服务,自动生成一个接口和实现类
→ 宿主需要添加对"System.ServiceModel"的引用以及WCF服务所在类库的引用
→ 在宿主的配置文件中配置<system.serviceModel>节点,以及end point等信息
→ 客户端程序添加对WCF服务的引用
→ 客户端在声明WCF服务代理类实例的时候,需要明确指出使用哪一个end point
就酱。