wsDualHttpBinding绑定类似于wsHttpBinding绑定,它额外支持双向通信但不支持传输级别的安全。双向通信通过两个形状改变绑定元素完成: OneWayBindingElement和CompositeDuplexBindingElement绑定元素。CompositeDuplexBindingElement绑定元素在两个单向信道上加了一个双向通信信道。wsDualHttpBinding绑定使用HttpTransportBindingElement绑定元素。这是传输仅支持请求-回复消息交换模式。OneWayBindingElement 绑定元素允许HttpTransportBindingElement绑定元素与CompositeDuplexBindingElement绑定元素一起使用。
wsDualHttpBinding绑定不支持传输层次的安全。这意味着在使用wsDualHttpBinding绑定时不可以使用SSL/TLS加密。
下面的代码显示了wsDualHttpBinding绑定的地址格式
http://{hostname}:{port}/{service location}
http的默认端口是80。这是任何基于HttpTransportBindingElement绑定元素的绑定的情形,包括wsDualHttpBinding绑定。
表4.10 列出了wsDualHttpBinding绑定可以设定的绑定属性。
表4.10 wsDualHttpBinding绑定属性
我们已经为wsDualHttpBinding绑定修改了StockQuoteService应用来支持双向通信。列表4.17显示了StockQuoteDualService实现。服务支持使用由IStockQuoteDuplexService契约确定的IStockQuoteCallback契约来进行双向消息交换模式。
列表4.17 IStockQuoteDuplexService, IStockQuoteCallback,and StockQuoteDuplexService
namespace EssentialWCF
{
public interface IStockQuoteCallback
{
[OperationContract(IsOneWay = true)]
void SendQuoteResponse(string symbol, double price);
}
[ServiceContract(CallbackContract=typeof(IStockQuoteCallback), SessionMode= SessionMode.Required)]
public interface IStockQuoteDuplexService
{
[OperationContract(IsOneWay = true)]
void SendQuoteRequest(string symbol);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class StockQuoteDuplexService : IStockQuoteDuplexService
{
#region IStockQuoteDuplexService Members
public void SendQuoteRequest(string symbol)
{
double value;
if (symbol == "MSFT")
{
value = 31.15;
}
else if (symbol == "YHOO")
{
value = 28.10;
}
else if (symbol == "GOOG")
{
value = 450.75;
}
else
value = double.NaN;
OperationContext ctx = OperationContext.Current;
IStockQuoteCallback callback = ctx.GetCallbackChannel<IStockQuoteCallback>();
callback.SendQuoteResponse(symbol, value);
}
#endregion
}
}
我们必须为我们的例子改变自我寄宿代码因为我们改变了我们用来支持双向消息的实现。列表4.18 显示了为StockQuoteDuplexService服务的寄宿代码。
列表4.18 StockQuoteDuplexService ServiceHost 服务
internal class MyServiceHost
{
internal static ServiceHost myServiceHost = null;
internal static void StartService()
{
myServiceHost = new ServiceHost(typeof(EssentialWCF.StockQuoteDuplexService));
myServiceHost.Open();
}
internal static void StopService()
{
if (myServiceHost.State != CommunicationState.Closed)
{
myServiceHost.Close();
}
}
}
列表4.19的配置信息使用wsDualHttpBinding绑定暴露StockQuoteDuplexService服务。
列表4.19 wsDualHttpBinding 宿主配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="NewBehavior" name="EssentialWCF.StockQuoteDuplexService">
<endpoint address="" binding="wsDualHttpBinding" bindingConfiguration=""
contract="EssentialWCF.IStockQuoteDuplexService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/stockquoteservice" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
列表4.20显示了配置信息是客户端用来调用使用wsDualHttpBinding绑定并实现IStockQuoteDuplexService契约的服务的。clientBaseAddress确定了客户端要监听回调消息的终结点。
列表4.20 wsDualHttpBinding客户端配置
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding name="SpecifyClientBaseAddress" clientBaseAddress="http://localhost:8001/client" />
<binding name="WSDualHttpBinding_IStockQuoteDuplexService" 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">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" />
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/stockquoteservice" binding="wsDualHttpBinding"
bindingConfiguration="WSDualHttpBinding_IStockQuoteDuplexService"
contract="ServiceReference.IStockQuoteDuplexService" name="WSDualHttpBinding_IStockQuoteDuplexService">
<identity>
<userPrincipalName value="Administrator@base.com" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
客户端应用程序在列表4.21显示。客户端实现IStockQuoteDuplexServiceCallback接口来接收服务端的回调消息。客户端应用程序使用InstantContext类向IStockQuoteDuplexServiceCallback接口传递一个引用。InstantContext类被传递给客户端代理的构造函数。
列表4.21 wsDualHttpBinding客户端应用
class Program : IStockQuoteDuplexServiceCallback
{
private static AutoResetEvent waitForResponse;
static void Main(string[] args)
{
string symbol = "MSFT";
waitForResponse = new AutoResetEvent(false);
InstanceContext callbackInstance = new InstanceContext(new Program());
using (StockQuoteDuplexServiceClient client = new StockQuoteDuplexServiceClient(callbackInstance))
{
client.SendQuoteRequest(symbol);
waitForResponse.WaitOne();
}
Console.ReadLine();
}
#region IStockQuoteCallback Members
public void SendQuoteResponse(string symbol, double price)
{
Console.WriteLine("{0} @ ${1}", symbol, price);
waitForResponse.Set();
}
#endregion
}
列表4.2显示了由svcutil.exe生成的客户端代理。客户端代理和先前实现的最大不同时客户端继承自DuplexClientBase类而不是ClientBase类。DuplexClientBase类添加了对双向通信的支持。
列表4.2 wsDualHttpBinding客户端代理
namespace Client.ServiceReference {
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference.IStockQuoteDuplexService", CallbackContract=typeof(Client.ServiceReference.IStockQuoteDuplexServiceCallback), SessionMode=System.ServiceModel.SessionMode.Required)]
public interface IStockQuoteDuplexService {
[System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://tempuri.org/IStockQuoteDuplexService/SendQuoteRequest")]
void SendQuoteRequest(string symbol);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IStockQuoteDuplexServiceCallback {
[System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://tempuri.org/IStockQuoteDuplexService/SendQuoteResponse")]
void SendQuoteResponse(string symbol, double price);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface IStockQuoteDuplexServiceChannel : Client.ServiceReference.IStockQuoteDuplexService, System.ServiceModel.IClientChannel {
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class StockQuoteDuplexServiceClient : System.ServiceModel.DuplexClientBase<Client.ServiceReference.IStockQuoteDuplexService>, Client.ServiceReference.IStockQuoteDuplexService {
public StockQuoteDuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance) :
base(callbackInstance) {
}
public StockQuoteDuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName) :
base(callbackInstance, endpointConfigurationName) {
}
public StockQuoteDuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) :
base(callbackInstance, endpointConfigurationName, remoteAddress) {
}
public StockQuoteDuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(callbackInstance, endpointConfigurationName, remoteAddress) {
}
public StockQuoteDuplexServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(callbackInstance, binding, remoteAddress) {
}
public void SendQuoteRequest(string symbol) {
base.Channel.SendQuoteRequest(symbol);
}
}
}