WCF 回调操作 CallBack Opeartions
WCF回调的必要条件:
1、并不是所有的绑定都支持回调操作,只有具有双向通信能力的绑定才可以使用回调。比如HTTP是与连接无关的因此不能用于回调。这就说明我们不能使用BasicHttpBinding或WSHttpBinding进行回调。WCF中NetTcpBinding和NetNamedPipeBinding支持回调,因为从本质上讲TCP和IPC协议支持双向通信。WCF中wsDualHttpBinding也是支持回调的因为它实际上是设置了两个HTTP通道
定义回调契约
一个服务契约若要定义回调,必须专门定义一个用于回调的契约。一个服务契约最多包含一个回调契约,一个服务契约一旦定义了回调契约那客户端必须支持这个回调。那如何为一个服务契约定义回调呢?使用ServiceContract特性的CallBackContract特性,代码如下:
[ServiceContract(CallbackContract = typeof(ISomeCallbackContract))]
public interface IService1
{
[OperationContract(IsOneWay=true)]
void DoSomething();
}
//用于回调的契约
interface ISomeCallbackContract
{
[OperationContract(IsOneWay=true)]
void SomeCallbackMethod();
[OperationContract(IsOneWay = true)]
void SomeCallbackMethod2();
}
客户端回调设置
客户端需要实现服务端定义的那个用于回调的契约ISomeCallbackContract,然后实例化回调对象再通过它创建一个上下文对象InstanceContext。然后用代理把这个回调的引用传回服务端
下面通过代码实例说明:
服务端代码:
[ServiceContract(CallbackContract = typeof(ISomeCallbackContract))]
public interface IService1
{
[OperationContract(IsOneWay=true)]
void DoSomething();
}
//用于回调的契约
interface ISomeCallbackContract
{
[OperationContract(IsOneWay=true)]
void SomeCallbackMethod();
[OperationContract(IsOneWay = true)]
void SomeCallbackMethod2();
}
实现服务的类
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class Service1 : IService1
{
static List<ISomeCallbackContract> m_Callbacks = new List<ISomeCallbackContract>();
public void DoSomething()
{
//获得由客户端传入的回调终结点的引用,使用这个引用可以调用客户端方法
ISomeCallbackContract callback = OperationContext.Current.GetCallbackChannel<ISomeCallbackContract>();
if (m_Callbacks.Contains(callback) == false)
{
m_Callbacks.Add(callback);
}
Trace.WriteLine("DoSomething");
//调用客户端实现的SomeCallbackMethod2方法,
callback.SomeCallbackMethod2();
CallClients();
}
static public void CallClients()
{
Action<ISomeCallbackContract> invoke = delegate(ISomeCallbackContract callback)
{
callback.SomeCallbackMethod();
};
m_Callbacks.ForEach(invoke);
}
}
客户端代码:
//客户端实现回调接口,使用VS自动生成代理的时候,对于回调接口的命名默认是以服务契约接口名称+Callback,而不是原先在服务端定义的回调接口的名子
public class CallBack : ServiceReferenceCallBack.IService1Callback
{
public void SomeCallbackMethod()
{
MessageBox.Show("SomeCallbackMethod()", "MyClient");
}
public void SomeCallbackMethod2()
{
MessageBox.Show("SomeCallbackMethod2()", "MyClient");
}
}
客户端调用
首先生成回调接口实现的对象,然后用这个对象去实例化一个上下文对象context ,在创建客户端代理对象的时候在构造方法中使用这个上下文对象context 就可以把客户端回调对象的引用作为消息的一部分传送到服务端,在服务端就可以通过OperationContext.Current.GetCallbackChannel<ISomeCallbackContract>()来获得这个回调对象的引用,服务端有了这个回调对象的引用就可以去调用客户端实现的回调方法
ServiceReferenceCallBack.IService1Callback callback = new CallBack();
InstanceContext context = new InstanceContext(callback);
ServiceReferenceCallBack.Service1Client sc = new WCFPrograme.ServiceReferenceCallBack.Service1Client(context);
sc.DoSomething();
总结:
1、必须使用支持双向通信的绑定如NetTcpBinding、NetNamedPipeBinding、WSDualHttpBinding
2、用 CallbackContract 为服务定义回调,回调的接口在服务端定义。
例:
[ServiceContract(CallbackContract = typeof(ISomeCallbackContract))]
3、服务端用 OperationContext.Current.GetCallbackChannel<T>()获取调用当前操作的客户端实例通道
例:
ISomeCallbackContract callback = OperationContext.Current.GetCallbackChannel<ISomeCallbackContract>()
4、服务端获得客户端实例通道的对象以后就可以调用客户端方法
例:
callback.SomeCallbackMethod2();
5、客户端生成代理以后,由客户端去实现回调接口
例:
public class CallBack : ServiceReferenceCallBack.IService1Callback
{
//实现接口
}
6、客户端创建回调的对象并生成操作调用的上下文并由客户端代理对象传回服务端
例:
ServiceReferenceCallBack.IService1Callback callback = new CallBack();
InstanceContext context = new InstanceContext(callback);
ServiceReferenceCallBack.Service1Client sc = new WCFPrograme.ServiceReferenceCallBack.Service1Client(context);
sc.DoSomething();
7、配置文件:使用wsDualHttpBinding的时候,需要在客户端配置文件中需要指定客户端基址clientBaseAddress,服务可以通过这个地址与客户端取得联系,这是因为wsDualHttpBinding需要两条http通道,而是使用netTcpBinding时不需要
例:
<endpoint address="http://localhost:8731/Design_Time_Addresses/WcfCallBack/Service1/"
binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IService1"
contract="ServiceReferenceCallBack.IService1" name="WSDualHttpBinding_IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_IService1" 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"
clientBaseAddress="http://localhost:8008">
<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>
正面猛男