Server 对Client的回调是很有用的, 比如Client向服务器提出一个请求, 而那个操作服务器需要处理很长时间, 如果不使用Callback, Client必须阻塞线程并等待服务器的响应, 但如果使用异步Callback, Client可以在提出请求后立刻返回当前线程, 而服务器则再操作完成之后执行Client自定义的回调方法.
WCF对于这种服务器回调客户端提供了非常好的支持, 但这只支持NetTcpBinding和NamedPipeBinding, 对于Http是不支持的...
下面使用一个简单的NetTcpBinding来示例如何建立和使用服务器异步回调的服务.
首先定义服务器的Contract:
[ServiceContract(CallbackContract = typeof(IBankServiceCallback))]
public interface IBankService
{
[OperationContract(IsOneWay = true)]
void LongTimeOperation();
}
这个服务叫BankService, 但它只有一个LongTimeOperation方法. CallbackContract 定义了回调的接口, 注意一个ServiceContract只能有个CallbackContract. 而把方法定义成IsOneWay是异步调用所必须的.
回调接口:
public interface IBankServiceCallback
{
[OperationContract]
void OperationComplete(DateTime time);
}
这个回调接口需要Client实现, 但服务器端必须知道如何调用Callback, 所以定义一个接口是必须的.
BankService的实现:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class BankService : IBankService
{
#region IBankService Members
public void LongTimeOperation()
{
Thread.Sleep(10000); //模拟长时间操作...
IBankServiceCallback callback = OperationContext.Current.GetCallbackChannel<IBankServiceCallback>();
callback.OperationComplete(DateTime.Now);
}
#endregion
}
ConcurrencyMode = ConcurrencyMode.Reentrant 这句指定了服务的方法可以被重复调用(但是是单线程的), 如果不指定, 则默认是 ConcurrencyMode.Single , 导致在回调过程产生死锁..
IBankServiceCallback callback = OperationContext.Current.GetCallbackChannel<IBankServiceCallback>() 这句获得可以调用Client实现的Callback方法的代理.
下面是Client对Callback接口的实现. 当服务器回调时, 客户端将会调用这个方法:
public class MyCallback : IBankServiceCallback
{
#region IBankServiceCallback Members
public void OperationComplete(DateTime time)
{
Console.WriteLine("OperationComplete @ " + DateTime.Now.ToLongTimeString());
}
#endregion
}
OK..该定义的都定义了, 下面用一个Main函数模拟服务器和客户端的交互:
[STAThread]
static void Main()
{
string uriStr = "net.tcp://localhost:10101/BankService/";
Uri uri = new Uri(uriStr);
NetTcpBinding tcp = new NetTcpBinding();
EndpointAddress endpoint = new EndpointAddress(uriStr);
ServiceHost service = new ServiceHost(typeof(BankService), uri);
service.AddServiceEndpoint(typeof(IBankService), tcp, uri);
service.Open();
Console.WriteLine("service ok, 'enter' to continue...");
Console.ReadLine();
InstanceContext context=new InstanceContext(new MyCallback());
DuplexChannelFactory<IBankService> duplexCF = new DuplexChannelFactory<IBankService>(context, tcp, endpoint);
IBankService serverSvc = duplexCF.CreateChannel();
Console.WriteLine("client ok, 'enter' to continue...");
Console.ReadLine();
Console.WriteLine("start @ " + DateTime.Now.ToLongTimeString());
serverSvc.LongTimeOperation();
Console.WriteLine("server processing...");
Console.ReadLine();
}
注意斜体部分, 首先建立一个InstanceContext, 包含了回调方法的实现对象, 然后使用DuplexChannelFactory建立服务对象.
运行结果如下:
service ok, 'enter' to continue...
client ok, 'enter' to continue...
start @ 13:43:56
server processing...
OperationComplete @ 13:44:06
OVER~