[WCF学习笔记] Binding

来源:互联网 发布:云南省大数据平台 编辑:程序博客网 时间:2024/04/29 16:54

我们已经知道,WCF的客户端通过Endpoint来访问WCF服务端的服务,也就是说,WCF Service Provider将WCF service通过Endpoint暴露出来供Service consumer调用。

而每个Endpoint包含3个主要要素:Addressbindingcontract。其中,Address提供了每个Endpoint的唯一地址;Contract具体指定了这个服务提供什么功能,Client和Server交互的输入输入,消息格式,其它约定等。而真正实现了通信功能的则是Binding

 

WCF中实现通信功能的binding很简单,就两步:

  1. 根据需要,选择/创建合适的binding类型,例如WSHttpBinding,WSDualHttpBinding或NetTcpBinding;或者创建您自定义的binding;(WCF预定义了9种类型供我们选择)
  2. 创建实现binding的Endpoint,可以通过代码实现,或配置文件配置。如果Endpoint是代码实现的,则Endpoint必须要加到ServiceHost实例。

 

Binding真正实现了连接到Endpoint的通信细节。具体到通信的处理内部,可能很简单,也可能很复杂

  • Transport (传输协议)
  • Encoding (消息编码)
  • Protocol (安全性,可靠消息传递和事务)

 

Binding类型的选择

WCF中实现通信功能的binding很简单的原因之一是WCF已经预定义了9种常用的通信类型。那我们实际项目中,如何选择正确的类型?

  1. BasicHttpBinding: 默认关闭security,不支持WS-*,不支持SOAP安全和事务。类似以前的ASPX Web Service,使用HTTP,使用Txt/XML作消息编码。
  2. WSHttpBinding:支持WS-*,支持SOAP安全和事务。支持HTTP和HTTPS。
  3. WSDualHttpBinding:相比WSHttpBinding,支持duplex services - which provides the ability for a service to communicate back to the client via a callback。支持reliable sessions和communication via SOAP intermediaries。
  4. WSFederationHttpBinding:支持WS-Federation protocol,提供良好的可信任的Authentication和授权。
  5. NetTcpBinding:提供安全、可靠的.net到.net的跨主机的TCP通信。支持SOAP安全、事务和可靠性。使用二进制编码。
  6. NetNamedPipeBinding:提供安全、可靠的同主机跨进程的命名管道通信。支持SOAP安全、事务和可靠性。使用二进制编码。
  7. NetMsmqBinding:提供安全、可靠的.net到.net的跨主机的MSMQ通信。使用二进制编码。(可支持disconnected operation不连接的操作)
  8. NetPeerTcpBinding:提供安全的点到点的通信。支持SOAP安全、事务和可靠性。使用二进制编码。
  9. MsmqIntegrationBinding:用来集成legacy的MSMQ和WCF。使用二进制编码。

 

WCF_Binding_thumb

其中,已Net开头的类型不具备互操作性,只限于.net到.net平台通信。而其他的则是WebService绑定,具有互操作性。

下图是选择的建议:

020308_0348_WCFBindi5

 

Binding如何实现通信细节 - Channel layer - Channel stacks

Channel - A channel, is the medium through which messages are exchanged.

例如,通常的消息收发过程:

  1. 客户端建立一个channel到服务端
  2. 服务端accept客户端的请求,打开一个channel
  3. 客户端通过这个channel发生request消息
  4. 服务端通过这个channel reply应答到客户端

Channel Stacks - 一连串的channel来处理消息,分别处理消息的安全性、互操作性、消息pattern、消息传输等任务。不管Channel具体完成怎样的功能,他们都可以看成是一个个Message处理器,这包括为了某种需求添加、修改Soap header;压缩整个Message、或者Message body; 对Message进行签名或者加密等等。例如,最底层的transport channel负责消息收发,上面的protocol channels提供通信功能和消息操作等。

Channel stacks是使用工厂模式创建的。消息发送端创建一个ChannelFactory,在binding的接收端创建一个IChannelListener来侦听incoming message。ChannelFactory创建一个channel stack,用它application就可以用来发消息。IChannelListener收到消息后交给侦听的application,创建channel stack。

 

Client Channel

例如一个WCF客户端,使用channel级编程实现步骤:

  1. 创建一个binding
  2. 创建/build一个channel factory
  3. 创建一个channel
  4. 发送请求
  5. 读取应答
  6. 关闭channel对象
CustomBinding cb = new CustomBinding ();
 
cb.Elements.Add(new TcpTransportBindingElement());
 
IChannelFactory<IRequestChannel> cf =
  cb.BuildChannelFactory<IRequestChannel>(new BindingParameterCollection());
 
cf.Open();
 
IRequestChannel chnl = cf.CreateChannel(new
    EndpointAddress(“net.tcp//localhost:8000/CoolApp”));
 
Message reqmess =
  Message.CreateMessage(MessageVersion.Soap12WSAddressing10,
   “http://wrox.com/requestaction”, “Message body data”);
 
Message repmess = chnl.Request(reqmess);
 
textbox1.text = “Sending message...”;
 
string MessageData = repmess.GetBody<string>();
 
textbox2.text = MessageData;
 
reqmess.Close();
repmess.Close();
chnl.Close();
cf.Close();

 

Service Channel

例如一个WCF服务端,使用channel级编程实现步骤:

  1. 创建一个binding
  2. 创建一个channel listener
  3. 打开channel listener
  4. 读取请求,发送应答
  5. 关闭channel对象
CustomBinding cb = new CustomBinding();
 
cb.Elements.Add(new TcpTransportBindingElement());
 
IChannelListener<IReplyChannel> lis =
  cb.BuildChannelListener<IReplyChannel>(new
    Uri(“net.tcp//localhost:8000/CoolApp”),
      new BindingParameterCollection());
 
lis.Open();  
 
IReplyChannel repchnl = lis.AcceptChannel();
 
repchnl.Open();
 
RequestContext rc = repchnl.ReceiveRequest();
 
Message reqmes = rc.RequestMessage;
Message repmes = Message.CreateMessage(MessageVersion.Soap12WSAddressing10,“”,“”);
 
rc.Reply(repmes);
 
reqmes.Close();
rc.Close();
repchnl.Close();
lis.Close();

 

扩展channel,Extend channel,创建自定义的channel

有以下几个步骤来创建自定义的channel

  1. 选择合适的MEP (Message Exchange Pattern)
  2. 创建channel factory和listener。Channel listeners是在Server端创建channels用来侦听和收消息,channel factory是在发送端/客户端创建channels用来发消息。
  3. 加上binding element
  4. 处理异常

 

选择合适的MEP (Message Exchange Pattern),有3种MEP供选择:

  • Datagram: 客户端发一个消息不期望应答。客户端并且也不能保证接受方收到了消息。
  • Request-Response:客户端发一个消息,并收到一个应答。传统的Request-Reply 消息交换模式。
  • Duplex:客户端可以发任意多个消息,并按任意次序收到应答。双向Duplex消息交换模式。

 

Chanel listeners (收)

Channel listener在服务端创建Channel,侦听消息,从下层接收消息,把消息放到队列,channel各自从队列拿消息,处理消息,最后通过channel把消息交给上层。

 

Chanel factories (发)

Channel factory在发送端/客户端创建channels, 用来发消息。Channels从上层拿到消息,处理消息,然后交给下层。Channel factory也要负责关闭自己创建的channels。

 

加上binding element,创建IChannelFactory和IChannelListener对象

一个Binding由BindingElement collection组成, 构成BindingElement collection的元素是一个个的BindingElement。BindingElement的最重要的功能就是创建IChannelFactory和IChannelListener对象。

//the following code uses the BuildChannelFactory of type IRequestChannel with a TcpTransportBindingElement:
CustomBinding cb = new CustomBinding();
 
TcpTransportBindingElement el = new TcpTransportBindingElement();
 
BindingParameterCollection bpc = new BindingParameterCollection();
 
BindingContext bc = new BindingContext(cb, bpc);
 
//Create channel factory
IChannelFactory<IRequestChannel> factory =
    el.BuildChannelFactory<IRequestChannel>(bc);
 
factory.Open();
 
EndpointAddress ea = new
  EndpointAddress(“net.tcp://localhost:8000/CoolChannelApp”);
 
IRequestChannel reqchan = factory.CreateChannel(ea);
 
reqchan.Open();
 
Message request =
   Message.CreateMessage(MessageVersion.Default, “this stuff rocks!”);
 
Message response = reqchan.Request(request);
 
textBox1.Text = response.Headers.Action.ToString;
reqchan.Close();
factory.Close();

 

//the following code uses the BuildChannelListener of type IReplyChannel for accepting channels.
The return value is the IChannelListener of type IChannel from the context:
CustomBinding cb = new CustomBinding();
 
TcpTransportBindingElement el = new TcpTransportBindingElement();
 
BindingParameterCollection bpc = new BindingParameterCollection();
 
Uri ba = new Uri(“net.tcp://localhost:8000/CoolChannelApp”);
 
String relativeAddress = “net.tcp://localhost:8000/CoolChannelApp/WCFService”;
 
BindingContext bc = new BindingContext(cb, bpc, ba, relativeAddress,ListenUriMode.Explicit);
 
//Create channel listener
IChannelListenr<IReplyChannel> listen =
   el.BuildChannelListener<IReplyChannel>(bc);
 
listen.Open();
 
IReplyChannel repchan = listen.AcceptChannel();
 
repchan.Open();
 
RequestContext rc = repchan.ReceiveRequest();
 
Message message = rc.RequestMessage;
 
if (message.Headers.Action == “this stuff rocks!”)
{
   Message replymessage = Message.CreateMessage(MessageVersion.Default, “I KNOW!”);
   rc.Reply(replymessage);
}
message.Close();
repchan.Close();
listen.Close();