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~

Posted on 2006-09-28 13:48  Adrian H.  阅读(696)  评论(2编辑  收藏  举报