一、wcf托管服务的方式:
1):IIS5/6托管:与经典的webservice托管类似,把服务当成web项目,需要提供svc文件,其缺点是只能使用http协议,也就是说,只能使用单调服务,没有会话状态。IIS5还受端口的限制(所有服务必须使用相同的端口),主要优势是:客户端第一次请求是自动启动宿主进程。
2):was托管,全称windows激活服务,可托管网站,可托管服务,可使用任何协议,可以单独安装和配置,不依赖IIS7(一般情况在IIS7+中使用)。需要提供svc文件或在配置文件内提供等价的信息。
3):自托管,开发者提供和管理宿主进程生命周期的一种方法。可使用控制台程序,窗口程序,wpf程序提供宿主服务。可使用任意协议。必须先于客户端启动。可以实现wcf高级特性:服务总线,服务发现,单例服务。
二、现在实现一个“控制台类型”的自托管服务(使用TCP协议),项目架构如下(下载项目):
1)定义服务接口文件代码:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace WcfService { // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。 [ServiceContract] public interface IMyService { [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; } } } }2)实现服务类文件代码:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace WcfService { //默认为会话服务 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class MyService : IMyService { int InvokerCount = 0; public string GetData(int value) { InvokerCount++; return string.Format("CurrentInvokerCount:" + InvokerCount + " You entered: {0}", value); } public CompositeType GetDataUsingDataContract(CompositeType composite) { if (composite == null) { throw new ArgumentNullException("composite"); } if (composite.BoolValue) { composite.StringValue += "Suffix"; } return composite; } } }
3)main函数所在的程序入口文件代码(绑定NetTCP协议)(该功能也可以使用配置文件实现详情见附录A 也可以绑定Http协议,详情见附录B)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ServiceModel; using System.ServiceModel.Description; using WcfService; namespace WcfSelf { class Program { static void Main(string[] args) { Console.Title = "WCF服务器端"; /*定义ServiceHost对象,创建宿主 * 参数1:实现契约的类 * 参数2:为可选参数,Param类型的,定义了基址 */ using (ServiceHost host = new ServiceHost(typeof(MyService), new Uri("net.tcp://127.0.0.1:1212/sv"))) { //使用Tcp协议 NetTcpBinding binding = new NetTcpBinding(); //安全性,一般为none binding.Security.Mode = SecurityMode.None; ServiceMetadataBehavior mb = new ServiceMetadataBehavior(); // 服务元数据 mb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15; mb.HttpGetEnabled = true; //是否可以查看元数据.此处可以false,那么HttpGetUrl就可以为空 mb.HttpGetUrl = new Uri("http://127.0.0.1:8008/meta"); //元数据的地址,HttpGetUrl 必须是相对 URI 或方案为“http”的绝对 URI。 host.Description.Behaviors.Add(mb); //使用上面定义的Behaviors--服务元数据 //endpoint: A:URI(相对与绝对的URI都行),需要与协议匹配,B:绑定的协议,C:实现契约的类型 host.AddServiceEndpoint(typeof(IMyService), binding, ""); //指定用于通过 TCP 进行的 WS-MetadataExchange (WS-MEX) 消息交换的绑定的设置 host.AddServiceEndpoint( ServiceMetadataBehavior.MexContractName,MetadataExchangeBindings.CreateMexTcpBinding(),"mex");//参数三不能为空或"" //添加打开的匿名函数,没啥实际作用,就是提示 host.Opened += (sender, arg) => { Console.WriteLine("服务已启动。"); }; try { //打开服务 host.Open(); Console.Read(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } } }
三、创建一个”客户端“控制台程序,用来测试调用服务
1)启动服务:运行服务程序生成的exe文件,提示如下图:
2)创建客户端控制台程序,添加服务引用:
3)主文件中main的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestClient { class Program { static void Main(string[] args) { sr.MyServiceClient sc = new sr.MyServiceClient(); Console.WriteLine( sc.GetData(212121)); Console.WriteLine(sc.GetData(123123)); Console.Read(); } } }运行结果:
OK 完成!下载示例项目:下载
附录A:
使用配置文件来配置服务端:
<system.serviceModel> <bindings> <!--绑定协议--> <netTcpBinding> </netTcpBinding> </bindings> <services> <!--name为实现契约的类 behaviorConfiguration为使用的behavior的name--> <service name="WcfCallBack.Service1" behaviorConfiguration="MyBehavior"> <host> <baseAddresses> <!--如果是IIS+was托管,这个基地址无效,如果是自托管,该地址有用了--> <add baseAddress="net.tcp://127.0.0.1:1314/MyService" /> </baseAddresses> </host> <!--终结点为空:使用IIS地址或基地址 契约:定义契约的类--> <endpoint address="" binding="netTcpBinding" contract="WcfCallBack.IService1" /> <!--元数据访问地址:使用相对地址说明元数据地址为IIS地址或基地址加上“mex”拼接一起--> <!--例如 net.tcp://pc-20131208ieuv:2248/Service1.svc/mex,该地址也是非http类的协议添加服务引用的时候使用的地址--> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" /> </service> </services> <behaviors> <serviceBehaviors> <!--behavior的name--> <behavior name="MyBehavior"> <!--无法获取元数据 强烈建议设为false--> <serviceMetadata httpGetEnabled="False" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
附录B:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ServiceModel; using System.ServiceModel.Description; using WcfService; namespace WcfSelf { class Program { static void Main(string[] args) { Console.Title = "WCF服务器端"; /*定义ServiceHost对象,创建宿主 * 参数1:实现契约的类 * 参数2:为可选参数,Param类型的,定义了基址 */ using (ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://127.0.0.1:1212/sv"))) { BasicHttpBinding binding = new BasicHttpBinding(); ServiceMetadataBehavior mb = new ServiceMetadataBehavior(); // 服务元数据 mb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15; mb.HttpGetEnabled = true; //是否可以查看元数据.此处可以false,那么HttpGetUrl就可以为空 mb.HttpGetUrl = new Uri("http://127.0.0.1:8008/meta"); //元数据的地址,HttpGetUrl 必须是相对 URI 或方案为“http”的绝对 URI。 host.Description.Behaviors.Add(mb); //使用上面定义的Behaviors--服务元数据 //endpoint: A:URI(相对与绝对的URI都行),需要与协议匹配,B:绑定的协议,C:实现契约的类型 host.AddServiceEndpoint(typeof(IMyService), binding, ""); //指定用于通过 [TCP] 进行的 WS-MetadataExchange (WS-MEX) 消息交换的绑定的设置,如果是http,不能要写这一句 //host.AddServiceEndpoint( ServiceMetadataBehavior.MexContractName,MetadataExchangeBindings.CreateMexTcpBinding(),"mex"); //添加打开的匿名函数,没啥实际作用,就是提示 host.Opened += (sender, arg) => { Console.WriteLine("服务已启动。"); }; try { //打开服务 host.Open(); Console.Read(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } } }