CSDN广告是越来越多了,所有博客笔记不再更新,新网址 DotNet笔记
一、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);
}
}
}
}
}