寄宿于CS程序的WCF服务
最近项目中,需要对外部程序提供服务接口,用来进行数据交互和部分设备控制。由于都是使用的.NET平台开发的,因此想到使用WCF服务。之前也用过WCF服务,但是当初使用的时候是通过IIS寄宿的,有些地方不太让人满意,一则是同一个软件要部署两个地方,CS的桌面程序和寄宿于IIS的WCF服务部分,二则是由于系统本身问题,使用的是SQLite数据库,无法实现多线程访问,造成了数据重复,容易造成数据不一致。所以现在准备把WCF服务寄宿于CS程序中,这样就解决了以上两个问题。
由于对WCF一知半解,只知道按部就班的使用,从明白过其中的道理,所以在网上找了些教程[1],实现了自己的想法,但其中碰到不少问题,想来还是记下,或许以后还有用处。
本文使用一个最简单的控制台应用程序,同时通过使用WcfTestClient.exe测试,来完成这个示例。
一、在代码中实现并配置WCF服务。
在VS2012中创建一个控制台应用程序项目CsWcf,并添加接口ICalculator作为契约接口、类CalculatorService作为服务实现此接口中的操作方法,代码如下:
1. ICalculator.cs--服务契约
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ServiceModel;
6:
7: namespace CsWcf
8: {
9: [ServiceContract]
10: public interface ICalculator
11: {
12: [OperationContract]
13: double Add(double x, double y);
14: }
15: }
2. CalculatorService.cs--服务实现
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ServiceModel;
6:
7: namespace CsWcf
8: {
9: public class CalculatorService:ICalculator
10: {
11: public double Add(double x, double y)
12: {
13: return x + y;
14: }
15: }
16: }
3. 在Program.cs中的Main函数中,添加如下代码,实现WCF服务的寄宿,并开启服务。
1: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
2: {
3: host.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "http://192.168.1.102:10002/CalculatorService");
4:
5: if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
6: {
7: ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
8: behavior.HttpGetEnabled = true;
9: behavior.HttpGetUrl = new Uri("http://192.168.1.102:10002/CalculatorService");
10: host.Description.Behaviors.Add(behavior);
11: }
12:
13: host.Opened += delegate
14: {
15: Console.WriteLine("CalculaorService已经启动,按任意键终止服务!");
16: };
17:
18: host.Open();
19: Console.Read();
20: }
4. F5调试执行,服务开启后,打开WcfTestClient,添加服务地址,可以正确调用Add服务函数。
但是,以上将服务相关配置写到代码中了,这不利于后面的安装和部署。所以需要将配置信息放置在config配置文件中。
二,使用配置文件动态配置WCF服务。
配置app.config文件,实现WCF服务的可配置性。在项目中添加应用程序配置文件,并添加以下内容:
1: <system.serviceModel>
2: <behaviors>
3: <serviceBehaviors>
4: <behavior name="metadataBehavior">
5: <serviceMetadata httpGetEnabled="true" httpGetUrl="http://192.168.1.102:10002/CalculatorService/metadata" />
6: </behavior>
7: </serviceBehaviors>
8: </behaviors>
9: <services>
10: <service behaviorConfiguration="metadataBehavior" name="CsWcf.CalculatorService">
11: <endpoint address="http://192.168.1.102:10002/calculatorservice" binding="wsHttpBinding"
12: contract="CsWcf.ICalculator" />
13: <endpoint address="http://192.168.1.102:10002/calculatorservice/MEX/" binding="mexHttpBinding"
14: contract="IMetadataExchange" />
15: </service>
16: </services>
17: </system.serviceModel>
同时需要删除Program.cs中的配置代码:
1: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
2: {
3: host.Opened += delegate
4: {
5: Console.WriteLine("CalculaorService已经启动,按任意键终止服务!");
6: };
7:
8: host.Open();
9: Console.Read();
10: }
F5调试执行,与刚刚的执行结果一样!此处要特别注意:配置节中的第二个endpoint,即MEX配置,一定要加上,否则会出现如下错误:
错误: 无法从 http://192.168.1.102:10002/CalculatorService 获取元数据如果是您有权访问的 Windows (R) Communication Foundation 服务,请检查是否已启用在指定地址发布元数据。有关启用元数据发布的帮助,请参阅 http://go.microsoft.com/fwlink/?LinkId=65455 上的 MSDN 文档。WS-Metadata Exchange 错误 URI: http://192.168.1.102:10002/CalculatorService 元数据包含无法解析的引用:“http://192.168.1.102:10002/CalculatorService”。 Sendera:BadContextToken无法处理消息。这很可能是因为操作“http://schemas.xmlsoap.org/ws/2004/09/transfer/Get”不正确,或因为消息包含无效或过期的安全上下文令牌,或因为绑定之间出现不匹配。如果由于未处于活动状态导致服务中止了该通道,则安全上下文令牌无效。若要防止服务永久中止闲置会话,请增加服务终结点绑定上的接收超时。HTTP GET Error URI: http://192.168.1.102:10002/CalculatorService 下载“http://192.168.1.102:10002/CalculatorService”时出错。 请求因 HTTP 状态 400 失败: Bad Request。
我在Artech的文章[1]中学习中并没有看到第二个终结点的配置,所以运行的时候一直出错,找了好久才找到这个解决办法,可以参考“WCF Part 4 : Make your service visible through metadata”这篇文章[2]。
三、其它机器访问权限限制的解决办法
以上两步骤,我们完成了WCF服务的寄宿、发布和访问,但是如果在其它计算机上访问此WCF服务时,会发现权限提示:
无法满足对安全令牌的请求,因为身份验证失败!
这个时候,默认的安全是计算机名和用户名做为权限要求。如果想让其他用户也可以随时访问到些WCF服务,就需要修改配置文件中的安全级别,设置<security>的mode为None就可以了。同时更改一个endpoint的bindingConfiguration="wsHttpBindingConfiguration"。
1: <bindings>
2: <wsHttpBinding>
3: <binding name="wsHttpBindingConfiguration" maxReceivedMessageSize="20971510">
4: <readerQuotas maxStringContentLength="20971520" maxArrayLength="20971520"/>
5: <security mode="None"/>
6: </binding>
7: </wsHttpBinding>
8: </bindings>
再次运行此服务,其它计算机上也可以访问此服务了。
总结:WCF用起来简单,“制作”起来貌似也很简单,和写其它的代码基本无异,但是真正使用起来的时候,却又经常碰到这样那样的问题,个人感觉WCF是一个极其精密的仪器,操作简单,但是仪器的操作步骤和顺序却比较麻烦,而且零部件不牢固,经常脱落损坏,所以要想更好的使用它,需要对它有个很好的了解才是。由于现在网络的存在,好多东西都是用的时候搜索一下,基本没有系统、深入的学习过,这对以后的学习和工作经验的积累都用处不太大。
[1] Artech, 我的WCF之旅(1):创建一个简单的WCF程序
[2] Dennis van der Stelt, WCF Part 4 : Make your service visible through metadata
---------------------------------------------