WCF讲解

因为项目的需要,开始学习WCF,对于WCF,本人只能算作刚刚开始学习,算入门级水平,整理这篇blog,希望给后来学习者一个借鉴,少走些弯路。

 

本篇文章从入门开始,先讲解基础知识,然后讲解实现方法,采用了对比的方式进行了介绍,最后说明一些其他方法。

 

一、基础知识

WCF(windows communication fundation),微软出的一套SOA的框架,它整合了微软之前的MQ,.NETREMOTING,web service等多种技术,但是整合的同时,确降低了开发的难度,这一点在刚开始学习的是否,并未体会,但是在实践的过程中,开始有了体会。

 

概念:

契约:也就是contract,包括 ServiceContract,OperationContract,DataContract,DataMember,MessageContract(这个暂时未用),FaultContract(暂时未用到)等等,这个其实就是一个特定,通知FRAMEWORK,声明了这些特性的类,或者成员,具有了WCF所要求的传输特性和公开特性。

 

EndPoint:也就是断点,在wcf当中,客户端和主机端的通信,采用的其实是端到端的通信方式,也就是要求每个服务端与客户端需要有一个监听的端口,那么这个模式,采用了EndPoint的端点设计方式,也就是说,要求服务端提供断点,客户端连接端点,从而实现了相互通信。

 

EndPoint包括三个成员,包括地址,绑定,契约,地址,很显然服务端需要发布一个地址,客户端需要连接这个地址才能够通信。绑定,这个概念的出现是为了方便传输控制,因为实际的世界中,分为局域网和广域网,那么传输时就需要考虑具体的适用协议,一般而言,局域网内传输建议tcp,广域网传输建议http。因此就出现了NetTcpBinding,wsHttpBinding,basicHttpBinding。契约,就是通知客户端,我这个端口,对外发布了一个什么样子的服务。

 

绑定名称                                                                        简要介绍                                                       所需.Net Framework版本

basicHttpBinding                                                    基于WS-I Basic Profile 1.1的Web服务                                  3.0

wsHttpBinding                                                       针对改进的Web服务的绑定,包括WS-Security,

WS-Transaction等元素                                                         3.0

wsDualHttpBinding                                                 提供双工通信的HTTP绑定                                                        3.0

webHttpBinding                                                      支持REST/POX服务的绑定,使用XML/JSON序列化                      3.0

netTcpBinding                                                          使用TCP传输协议在跨主机的局域网内使用,支持可靠性、

事务、安全等特性、并且该绑定被特别优化来支持WCF系统。

但是,使用该绑定需要确保通信双方都基于WCF构建,这里

并不符合SOA的原则                                                                3.0

netNamedPipeBinding                                             支持和netTcpBinding大致相同的特性,

但由于使用命名管道进行通信,所以通信不能跨越主机                      3.0

netMsmqBinding                                                     使用微软消息队列(MSMQ)协议来进行异步脱机的消息交互。

关于该绑定的交互方式,在本书的后续章节中有详细的介绍               3.0

netPeerTcpBinding                                                 使用P2P协议在网格中进行消息交互                                             3.0

msmqIntegrationBinding                                         该绑定可以用来在WCF消息和MSMQ消息中进行转换                       3.0

wsFederationHttpBinding                                        该绑定支持使用了联合安全机制的Web服务                                   3.0

ws2007HttpBinding                                                该绑定继承自wsHttpBinding,其主要涉及目的是为了支持

2007年新制定的WS标准                                                            3.5

ws2007FederationHttpBinding                                 该绑定继承自wsFederationHttpBinding,和wsHttpBinding一样,

其设计目的是为了支持2007年新制定的WS标准

 

宿主:之所以存在这个概念,是因为所谓的WCF是无法单独存在的,必须寄宿在一个进程当中,对外发布服务和监听地址。所谓宿主其实就是一个进程。那么进程的模式可以是console程序,app程序,还可以是windows service服务,wcf还可以支持IIS的宿主,提供广域网特性支持。

 

元数据:服务的元数据描述服务的特征,外部实体需要了解这些特征以便与该服务进行通信。服务所公开的元数据包括XML架构文档(用于定义服务的数据协定)和WSDL文档(用于描述服务的方法)。启用元数据后,WCF通过检查服务及其终节点自动生成服务的元数据。

上述这段话是抄来的,但是阅读完之后的进一步理解是这样的,对于WCF服务而言,他需要向外部发布,然后客户端才可以使用,那么客户端如何才知道服务的基本特性哪,就是需要通过元数据的描述,将服务特性进行说明,特征包括了数据契约和服务契约。

 

二、实现方法

 

WCF的实现,其实是分为两个部分的,一个是服务端的实现,一个是客户端的实现。

 

服务端实现:从实现的策略上来讲,分为两种实现方式,可以通过vs2010的WCF模板实现WCF服务,也可以通过手工实现(手工实现也足够简单)。

 

1、VS2010的WCF模板实现,该方法实现的其实就是一个WCF的服务DLL,其中包括了接口和实现类,并未提供宿主,宿主的实现仍然需要自己实现。

2、手工实现,该方法其实就是添加类库程序,即普通的DLL,然后分别实现接口类和实现类即可。宿主也是要自己实现的。

 

宿主实现方式:一共分为两种方法,一种是IIS,一种是selfhost方案,IIS未涉及,暂时不讲解。

selfhost的方案:console和winservice方案,两者其实根本实现原理上是一样的。具体内容

方法一,通过APP.CONFIG实现宿主的实现,在APP.CONFIG当中,写入endpoint信息,和metadatebehavior信息,然后再程序中直接创建serviceHost对象创建。

方法二,不通过app.config实现,而是直接在程序中,创建servicehost所需要的,URI,ENDPOINTADDRESS信息,然后利用servicehost的构造函数,创建对象。

 

客户端的实现:方法有二:一种是通过app.config,然后在该文件当中写明具体的client标签,然后填写服务端的endpoint信息。另一种是通过程序代码提供的方法。

通过程序代码的方式实现也有两种方法,一种是使用ClientBase<T>集成接口,然后直接调用该对象实现。另一种是通过ChannelFactory的方案实现proxy代理。

关键代码如下:

 

public partial class CalculatorClient :System.ServiceModel.ClientBase<ICalculator>, ICalculator

{

public CalculatorClient()

{

}

public CalculatorClient(string endpointConfigurationName) :

base(endpointConfigurationName)

{

}

public CalculatorClient(string endpointConfigurationName, stringremoteAddress) :

base(endpointConfigurationName,remoteAddress)

{

}

public CalculatorClient(string endpointConfigurationName,System.ServiceModel.EndpointAddress remoteAddress) :

base(endpointConfigurationName, remoteAddress)

{

}

public CalculatorClient(System.ServiceModel.Channels.Binding binding,System.ServiceModel.EndpointAddress remoteAddress) :

base(binding, remoteAddress)

{

}

public double Add(double n1, double n2)

{

return base.Channel.Add(n1, n2);

}

}

 

在main方法中的使用:

class WCFClient

{

static void Main()

{

//Step 1: Create an endpoint address and an instance of the WCF Client.

CalculatorClient client = new CalculatorClient();

}

}

 

通道的方案如下:

 

ChannelFactory<ICalculateService>channel= newChannelFactory<ICalculateService>(“CalculateService”);

 

ICalculateService  proxy = channel.CreateChannle();

 

proxy.Add();

 

 

三、其他

 

服务契约:例子

 

using System.ServiceModel ;

 

[ServiceContract]

public interface IService1

{

[OperationContract]

string HelloWord(string name);

}

public class Service1 : IService1

{

public string HelloWord(string name)

{

return name + “说:HelloWord”;

}

}

 

数据契约例子:

using System.ServiceModel ;

using System.Runtime.Serialization ;

 

[DataContract]

public class People

{

[DataMember]

public string name;

[DataMember]

public int age;

 

public People(string name,int age)

{

this.name = name;

this.age = age;

}

}

 

数据契约使用

[ServiceContract]

public interface IService1

{

[OperationContract]

string GetInfomation(People people);

}

 

说明:对于enum类型其实是可以序列化的,因此可以直接应用到数据契约当中。对于自定义类型如果包含一个其他自定义类型,那么被包含的类型也必须支持数据契约,并使用KnowType(typeof())属性,确定该类型是可以被识别的。

 

服务契约,可以提供name属性,用于描述服务名称,namespace属性,用于描述服务的命名空间。

 

操作契约:

这是一种,oneway模式,就是客户端只需要调用,不需要知道返回值。并且是阻塞式的,需要等待执行的完成,才会返回。

[OperationContract(IsOneWay=true)]

void Test();

在请求响应模式中,客户端发送一个消息并且接收一个返回消息来完成一次交互。在该模式中,消息的发起端必然是客户端,并且从服务端返回只有一条消息。客户端在发送出消息后会阻止当前线程并且等待服务端返回消息。

[OperationContract]

void Test();

双工模式(Duplex)

在双工模式中,客户端和服务端都可以任意地向对方发送消息,而对方也可以以任意的次序来接收消息。在这种模式下,发送端和接收端的概念变得不再适用,取而代之的是通信的两个端点。

 

public interface ICallBack

{

[OperationContract(IsOneWay = true )]

void UpdateInterval(int seconds);

}

[ServiceContract(CallbackContract=typeof(ICallBack ))]

public interface IService1

{

[OperationContract]

void Heartbeat();

}

 

1.   在配置文件中设置绑定

<?xml version=”1.0″encoding=”utf-8″ ?>

<configuration>

<system.web>

<compilation debug=”true” />

</system.web>

<system.serviceModel>

<services>

<servicename=”WcfServiceLibrary1.Service1″behaviorConfiguration=”WcfServiceLibrary1.Service1Behavior”>

<endpoint address =”HelloWord ”

binding=”wsHttpBinding”

contract=”WcfServiceLibrary1.IService1″>

</endpoint>

</service>

</services>

</system.serviceModel>

</configuration>

 

2.   在代码中设置绑定

using System.ServiceModel;

using System.ServiceModel.Channels;

NetNamedPipeBinding binding = new NetNamedPipeBinding ();

EndpointAddress address = new EndpointAddress(newUri(“net.pipe://localhost/HelloWorld “));

using (HelloWorldProxy proxy = new HelloWorldProxy())

{

Console.WriteLine(proxy.HelloWorld(“Kevin”));       //利用代理调用服务

Console.Read();

}

 

 

WCF的异常处理:

 

[ServiceContract]

public interface IService1

{

[OperationContract]

int Add(int a, int b);

}

public class Service1 : IService1

{

public int Add(int a, int b)

{

throw new Exception(“错误”);

}

}

 

客户端是可以接收到异常信息的,WCF的通用异常处理方法是首先服务端将异常转换为SOAP,然后发送给客户端,客户端接收到后,再转换为exception类型。但是,通常情况下,是无法获得有意义的错误信息的。因此WCF当中,提供了一个叫做FaultContractAttribute的契约。

 

[ServiceContract]

public interface IService1

{

[OperationContract]

[FaultContract(typeof(FaultMessage))]

int Add(inta,int b);

}

 

[DataContract]

public class FaultMessage

{

[DataMember]

public string Message;

 

[DataMember]

public interrorCode;

}

public class Service1 : IService1

{

public int Add(int a, int b)

{

FaultMessage  faultMessage = newFaultMessage();

faultMessage.Message = “错误信息”;

faultMessage.errorCode = 1234;

throw new FaultException<FaultMessage>(faultMessage,faultMessage.Message);

}

}

 

客户端的异常信息

Unhandled Exception:System.ServiceModel.FaultException`1[Client.ServiceReferenc

e1.FaultMessage]: 错误信息 (FaultDetail is equal to Client.ServiceReference1.Fa

ultMessage).

注意:由于单程操作没有任何返回值,也不回把任何错误发还给客户端,所以不能在单程操作上使用错误契约,不然会导致运行时异常。

 

 

消息契约

使用数据契约已经足以应付消息交互双方对数据内容的所有要求,但是对消息的格式却无法全面控制。如果想要定制SOAP消息的Head和Body内容,这就需要定制SOAP消息的Head和Body内容,这就需要用到消息契约。消息契约在实际WCF编程中使用的并不多,本节不做详细讨论,只给出一个简单的示例:

[MessageContract ]

public interface MyMessage

{

[MessageHeader ]

public intSessionId;

[MessageHeader]

public string Description;

[MessageBodyMember ]

public string A;

[MessageBodyMember]

public string B;

[MessageBodyMember]

public string C;

}

说明:消息中包含Header跟Body两部分。Header用来存放上下文信息,Body用来存放数据信息。

 

 

关于WCF的session和instancecontextmode的实现对比表:

 

 

 

 

public enumSessionMode
{
Allowed = 0,   允许会话模式
Required = 1,  必须会话模式
NotAllowed = 2 必须不支持会话模式
}

 

public enumInstanceContextMode
{
PerSession =  0 ,   会话模式
PerCall = 1 ,    每次调用一个服务实例模式
Single = 2       单例模式
}

 

 

 

并发处理,其实是一个并发控制,本质是多线程

public enumConcurrencyMode
{
Single = 0,
Reentrant = 1,
Multiple = 2
}

 

 

Single并发模式
ConcurrentMode.Single并发模式是默认的并发设置,当设置了Single模式以后,WCF会为服务实例的操作提供同步锁。Single并发模式的策略较为极端,等同于在整个操作上加上了同步锁,在很多情况下,这并不是必须的。

Multiple并发模式
ConcurrentMode.Multiple表示WCF不会主动为服务操作添加任何锁,每个操作都允许客户端多个请求同时访问,这样做的好处是提高了系统的运行效率,防止消息被阻塞。
Reentrant并发模式
本质上,ConcurrentMode.Reentrant与ConcurrentMode.Single模式一样:在同一时间只允许一个线程访问服务操作,在访问进入操作之前必须获得同步锁。所不同的是,ConcurrentMode.Reentrant模式解决了ConcurrentMode.Single模式的死锁问题。
实际上ConcurrentMode.Reentrant模式的设计正是为了解决死锁问题。当服务操作在调用其它服务,或者回调客户端操作时,会释放服务端的同步锁,这样就能够保证在回调消息返回或者调用链回到服务端时不会发生死锁。在一些消息量较大,服务操作中较长时间访问其它操作的情况下,ConcurrentMode.Reentrant模式能够有效的保证配对消息能够在服务操作向外调用时进入操作。

 

WCF回调

 

回调处理,相信很多人都可能会用到,主要的目的是完成客户端和服务端的相互调用,WCF肯定是会考虑这个问题的。那么对于WCF的回调处理,其本质的想法是,一个流程的处理。

 

 

 

总结:

1、 必须使用支持双向通信的绑定如NetTcpBinding、NetNamedPipeBinding,注意wxHttpBinding是不支持的。

2、 要在服务契约定义的服务接口中,定义回调接口。

例:

 [ServiceContract(CallbackContract = typeof(ISomeCallbackContract))]

  Public interface ICalculateService

{

}

 Public interface ISomeCallbackContract

{

}

3、在服务的实现类当中,需要获取该接口,并使用该接口,获取的方法其实跟获取sessionId的方法很相似,都是通过ISomeCallbackContract callback  =OperationContext.Current.GetCallbackChannel<ISomeCallbackContract >();callback.SomeCallbackMethod2();

4、客户端生成代理以后,由客户端去实现回调接口

例:

      public class CallBack : ISomeCallbackContract

      {

        //实现接口

       }

5、在客户端需要实现该接口,基本的实现方法,是首先实现Clientbase<>类的继承关系,然后通过base获取一个InstanceContext的具体实例,然后再通过构造函数就可以直接创建。

ISomeCallbackContract callback = new CallBack();

InstanceContext context = new InstanceContext(callback);

SampleServiceClientproxy= new SampleServiceClient();

sc.DoSomething();

标签