Windows 通信基础(Windows Communication Foundation)
Windows 通信框架(Windows Communication Framework,WCF)的目标是提供创建分布式应用程序的统一模型,其概念是创建一个服务,有点类似于网站服务,包含想要公开的功能,然后,以各种不同方法公开这个服务。例如,网站服务总是传递 XML 消息,但是,可以把 WCF 服务配置成既能传递 XML 消息,还能传递二进制数据;另外,WFC 服务可以托管在任何进程中,不仅仅是托管在网站服务器上。这就是说,我们可以创建桌面应用程序来监听入站消息,而不需要在桌面上没有安装网站服务器。
注意
WCF 是 .NET 框架 3 的一部分,提供了一组编程接口,随 Windows Vista 同时发行,且已经安装在操作系统中;也可以从 http://www.microsoft.com 上下载,安装在 Windows XP和 Windows 服务器 2003 上(http://blogs.msdn.com/b/dotnet/p/dotnet_sdks.aspx,原文提供的地址好像不能下载了;.NET 的安装也越来越复杂了)。WCF 使用的协议是基于一组扩展网站服务的规范,有时简称为 WS-* 协议,因为每一个协议通常都以 WS- 前缀命名,比如,WS-Security 和 WS-Reliability;这些协议有的已经标准化,有的即将标准化。为了开发 WCF,需要从 http://is.gd/4US4W 下载 .NET 框架 3 SDK。
在第一个示例(见清单 11-9)中,我们将构建一个简单的 WCF 服务,托管在网站服务器中,看起来更像一个简单的网站服务;然后,再改进这个服务,以展示 WCF 的有趣功能。为了创建在网站服务器上托管的 WCF 服务,与在“创建网站服务”一节中讨论的步骤相同,不同的是,不能托管在运行 Linux 的 Apache 上,因为 WCF 依赖于 Windows 的一些特定功能。
清单11-9 创建简单的 WCF 服务
namespace Strangelights.Services
open System.ServiceModel
// the service contract
[<ServiceContract
(Namespace =
"http://strangelights.com/FSharp/Foundations/WCFServices")>]
type IGreetingService =
[<OperationContract>]
abstract Greet : name:string -> string
// the service implementation
type GreetingService() =
interface IGreetingService with
member x.Greet(name) = "Hello: " + name
这个服务的定义分成两个部分,一个是描述服务契约的接口,另一个是契约的实现。所有 WCF 服务都以这种方法定义。这个接口名为 IGreetingService,公开一个函数 Greet。要使 WCF 契约有用,还必须用 System.ServiceModel.ServiceContractAttribute 标记接口,它应该包含服务的命名空间;在服务接口内部,打算公开的每一个函数都要用 OperationContractAttribute 去标记。每一个参数都有名字[ ,这一点很重要 ];在 F# 中,创建接口的参数可能没有名字,而是由它们的类型定义。作为 WCF 契约的接口,如果其函数没有参数,是能够编译的,但是,当你调用这个服务时会报错,因为在 WCF 框架中,这个参数名是用于(通过反射 reflection)创建在网络上发送的数据。GreetingService 类实现了契约,简单地提供一个问候语,把传递的名字加到“hello:”的后面。
为了把服务集成到网站服务器,需要创建 .svc 文件,它的作用与网站服务的 .asmx 文件相似,告诉网站服务器应该用什么类型来处理这个服务请求。在下面的示例中可以看到这个服务一致的 .svc 文件;注意,我们在这个示例中看到的就是完整的文件,这种文件通常只有一行。在 .svc 文件中最重要的特性是 Service,它告诉网站服务器应该使用哪一种类型:
<% @ServiceHost Debug="true" Service="Strangelights.Services.GreetingService" %>
最后,必须配置服务。因为 WCF 可以选择协议,所以要使用配置文件告诉它使用哪一个。清单 11-10 中的配置文件显示了能够用来配置服务的配置文件。Service 元素定义两个端点,这些协议是客户端用来和服务对话的。其中一个端点是标准的网站服务 HTTP 绑定,另一个是元数据交换绑定;它允许服务公开有关它自身的元数据,将告诉任何潜在的客户端应该如何与服务对话。当创建客户端代理时,将使用这个端点。
清单 11-10 WCF 服务的配置文件
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.serviceModel>
<services>
<service
name="Strangelights.Services.GreetingService"
behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint
contract="Strangelights.Services.IGreetingService"
binding="wsHttpBinding"/>
<endpoint
contract="Strangelights.Services.IGreetingService"
binding="mexHttpBinding" address="mex"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
为了创建这个服务的客户端,需要使用工具 SvcUtil.exe,它的目的与我们在“创建网站服务”一节讨论过的 wsdl.exe 工具相似。使用 SvcUtil.exe 创建服务的代理,用下面的命令行,适当地改变一下 URL:
svcutil.exe http://localhost:1033/WCFService/Service.svc?wsdl
这将生成一个 C# 代理文件,然后,可以把它编译成一个 .NET 程序集在 F# 中使用;它还生成一个 .config 文件,可以用于配置任何客户端的应用程序。
使用代理就变得非常简单了,添加了对这个代理的 .dll 文件的引用以后,只要创建代理的实例,就可以用适当的参数调用它的 Greet 方法了。清单 11-11 展示了代理的一个示例;因为调用代理的 Dispose 方法很重要,所以,实例化代理时,使用了 use 绑定[ 原文忘记修改了,还是第一版的内容。 ]。
清单 11-11 调用 WCF 服务
open System
let main() =
use client = new GreetingServiceClient()
while true do
printfn "%s" (client.Greet("Rob"))
Console.ReadLine() |> ignore
use client = new GreetingServiceClient()
printfn "%s" (client.Greet("Rob"))
Console.ReadLine() |> ignore
do main()
清单 11-12 是一个产生的配置文件的示例,已经删除了一些特定的内容,以保证示例运行得更加顺利。安全设置已经删除,因为如果运行在一台脱离域控制器的计算机上(在开发环境中很常见的!),可能会引起示例的失败。另外,产生的两个端点中有一个也已经删除,因此,在代码中就不需要指定端点了。
清单 11-12 调用 WCF 服务的配置文件
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IGreetingService"
closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32"
maxStringContentLength="8192"
maxArrayLength="16384"
maxBytesPerRead="4096"
maxNameTableCharCount="16384" />
<reliableSession ordered="true"
inactivityTimeout="00:10:00"
enabled="false" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8080/service"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IGreetingService"
contract="IGreetingService"
name="WSHttpBinding_IGreetingService">
</endpoint>
</client>
</system.serviceModel>
</configuration>
运行清单 11-11 产生的结果如下:
Hello: Rob